/*
 * ProjectHub client portal — shared layout, navigation, and auth-screen
 * styling. Independent from assets/css/admin.css: this is a separate
 * frontend surface, not the wp-admin screen, even though it borrows the
 * same "quiet business UI" tone and the projecthub- class prefix.
 *
 * Every selector is scoped under .projecthub-portal (or the
 * .projecthub-portal-nav block it explicitly extends) so this file can
 * never affect anything outside the portal's own templates, including
 * WordPress's own frontend/theme output on any other URL.
 */

:root {
	--projecthub-portal-bg: #f3f4f6;
	--projecthub-portal-surface: #ffffff;
	--projecthub-portal-border: #dcdcde;
	--projecthub-portal-text: #1f2933;
	--projecthub-portal-muted: #646970;
	--projecthub-portal-accent: #2271b1;
	--projecthub-portal-danger: #b32d2e;
	--projecthub-portal-success: #1a7f37;
	--projecthub-portal-sidebar-width: 220px;
	--projecthub-portal-header-height: 56px;
	--projecthub-portal-gantt-weekend-bg: var(--projecthub-portal-border);
	--projecthub-portal-gantt-today-bg: #eaf2fb;
}

/* -- base --------------------------------------------------------------- */

.projecthub-portal {
	background: var(--projecthub-portal-bg);
	box-sizing: border-box;
	color: var(--projecthub-portal-text);
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
	line-height: 1.5;
	margin: 0;
	overflow-x: hidden;
}

.projecthub-portal *,
.projecthub-portal *::before,
.projecthub-portal *::after {
	box-sizing: inherit;
}

.projecthub-portal a {
	color: var(--projecthub-portal-accent);
}

.projecthub-portal a:focus-visible,
.projecthub-portal button:focus-visible,
.projecthub-portal input:focus-visible {
	outline: 2px solid var(--projecthub-portal-accent);
	outline-offset: 2px;
}

/* Visually hides content while keeping it in the accessibility tree —
 * the standard clip-based technique (matches WordPress core's own
 * .screen-reader-text), reimplemented under the projecthub-portal-
 * prefix since this stylesheet is independent of wp-admin/theme CSS
 * (Client Portal Daily Grid Gantt: used to keep the Gantt row's
 * start/due date text available to assistive technology after it was
 * hidden visually, docs/26-client-portal-daily-grid-gantt-plan.md §21). */
.projecthub-portal-sr-only {
	border: 0;
	clip: rect(0, 0, 0, 0);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	white-space: nowrap;
	width: 1px;
}

/* -- authenticated shell: header + sidebar + main ------------------------ */

.projecthub-portal__shell {
	display: flex;
	flex-direction: column;
	min-height: 100vh;
	padding-top: var(--projecthub-portal-header-height);
}

.projecthub-portal__header {
	align-items: center;
	background: var(--projecthub-portal-surface);
	border-bottom: 1px solid var(--projecthub-portal-border);
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem 1rem;
	justify-content: space-between;
	left: 0;
	min-height: var(--projecthub-portal-header-height);
	padding: 0.5rem 1rem;
	position: fixed;
	right: 0;
	top: 0;
	z-index: 10;
}

.projecthub-portal__brand {
	font-size: 1.125rem;
	font-weight: 700;
}

.projecthub-portal__account {
	align-items: center;
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
}

.projecthub-portal__account-name {
	color: var(--projecthub-portal-muted);
	font-size: 0.875rem;
}

.projecthub-portal__account-link {
	color: var(--projecthub-portal-text);
	font-size: 0.875rem;
}

.projecthub-portal__logout-form {
	margin: 0;
}

.projecthub-portal__logout-button {
	background: transparent;
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 4px;
	color: var(--projecthub-portal-text);
	cursor: pointer;
	font-size: 0.875rem;
	padding: 0.4rem 0.9rem;
}

.projecthub-portal-notifications-form__option {
	display: block;
	margin: 1rem 0;
}

.projecthub-portal-notifications-form__submit {
	background: var(--projecthub-portal-accent);
	border: none;
	border-radius: 4px;
	color: #fff;
	cursor: pointer;
	font-size: 0.875rem;
	padding: 0.5rem 1.2rem;
}

.projecthub-portal__body {
	display: flex;
	flex: 1;
}

.projecthub-portal__main {
	flex: 1;
	min-width: 0;
	padding: 1.5rem;
}

.projecthub-portal__page-title {
	font-size: 1.5rem;
	margin: 0 0 1rem;
}

/* -- navigation ----------------------------------------------------------- */

.projecthub-portal-nav {
	align-self: flex-start;
	background: var(--projecthub-portal-surface);
	border-bottom: 1px solid var(--projecthub-portal-border);
	border-bottom-right-radius: 8px;
	border-right: 1px solid var(--projecthub-portal-border);
	flex-shrink: 0;
	position: sticky;
	top: var(--projecthub-portal-header-height);
	width: var(--projecthub-portal-sidebar-width);
	z-index: 5;
}

.projecthub-portal-nav__list {
	list-style: none;
	margin: 0;
	padding: 0.75rem 0;
}

.projecthub-portal-nav__item {
	margin: 0;
}

.projecthub-portal-nav__link {
	color: var(--projecthub-portal-text);
	display: block;
	padding: 0.6rem 1.25rem;
	text-decoration: none;
}

.projecthub-portal-nav__link:hover {
	background: var(--projecthub-portal-bg);
}

.projecthub-portal-nav__link.is-active {
	background: var(--projecthub-portal-bg);
	border-left: 3px solid var(--projecthub-portal-accent);
	color: var(--projecthub-portal-accent);
	font-weight: 600;
	padding-left: calc(1.25rem - 3px);
}

/* Project picker (Client Portal Sidebar Redesign): sits between the fixed
 * Home/案件 list above and the Project-scoped sub-list below, so its own
 * top/bottom border reads as separating three distinct sidebar sections. */
.projecthub-portal-nav__project-picker {
	border-bottom: 1px solid var(--projecthub-portal-border);
	border-top: 1px solid var(--projecthub-portal-border);
	padding: 0.75rem 1.25rem;
}

.projecthub-portal-nav__project-select {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 4px;
	color: var(--projecthub-portal-text);
	padding: 0.4rem 0.5rem;
	width: 100%;
}

.projecthub-portal-nav__project-select:focus {
	border-color: var(--projecthub-portal-accent);
	outline: 2px solid var(--projecthub-portal-accent);
	outline-offset: -1px;
}

/* Indented to read as nested beneath the picker above, rather than as a
 * second top-level section equal to the Home/案件 list. */
.projecthub-portal-nav__list--project .projecthub-portal-nav__link {
	padding-left: 2rem;
}

.projecthub-portal-nav__list--project .projecthub-portal-nav__link.is-active {
	padding-left: calc(2rem - 3px);
}

/* -- auth/message screens (login, maintenance, access-denied, 404) -------- */

.projecthub-portal-login__container,
.projecthub-portal-message__container {
	background: var(--projecthub-portal-surface);
	border-radius: 8px;
	box-shadow: 0 1px 3px rgb(0 0 0 / 12%);
	margin: 10vh auto;
	max-width: 360px;
	padding: 2rem;
}

.projecthub-portal-login__title {
	font-size: 1.25rem;
	margin: 0 0 1.5rem;
	text-align: center;
}

.projecthub-portal-login__form label {
	display: block;
	font-size: 0.875rem;
	margin-bottom: 0.25rem;
}

.projecthub-portal-login__form input[type="text"],
.projecthub-portal-login__form input[type="password"] {
	padding: 0.5rem;
	width: 100%;
}

.projecthub-portal-login__form button {
	background: var(--projecthub-portal-accent);
	border: none;
	border-radius: 4px;
	color: var(--projecthub-portal-surface);
	cursor: pointer;
	padding: 0.5rem 1rem;
	width: 100%;
}

.projecthub-portal-login__error {
	color: var(--projecthub-portal-danger);
}

.projecthub-portal-login__notice {
	color: var(--projecthub-portal-success);
}

/* -- project/requirement read views (Phase 42 Unit H) ---------------------- */

.projecthub-portal-back-link {
	display: inline-block;
	font-size: 0.875rem;
	margin-bottom: 1rem;
}

.projecthub-portal-badge {
	background: var(--projecthub-portal-bg);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 999px;
	display: inline-block;
	font-size: 0.8125rem;
	padding: 0.15rem 0.75rem;
}

.projecthub-portal-badge--archived {
	border-color: var(--projecthub-portal-muted);
	color: var(--projecthub-portal-muted);
}

.projecthub-portal-badge--attention {
	border-color: var(--projecthub-portal-accent);
	color: var(--projecthub-portal-accent);
}

.projecthub-portal-table-scroll {
	max-width: 100%;
	overflow-x: auto;
}

.projecthub-portal-table {
	border-collapse: collapse;
	width: 100%;
}

.projecthub-portal-table th,
.projecthub-portal-table td {
	border-bottom: 1px solid var(--projecthub-portal-border);
	padding: 0.6rem 0.75rem;
	text-align: left;
	white-space: nowrap;
}

.projecthub-portal-table caption {
	caption-side: top;
	font-weight: 600;
	padding-bottom: 0.5rem;
	text-align: left;
}

.projecthub-portal-version-history__row--current {
	background: var(--projecthub-portal-bg);
}

.projecthub-portal-detail-card {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	padding: 1.25rem;
}

.projecthub-portal-detail-list {
	margin: 0;
}

.projecthub-portal-detail-list dt {
	color: var(--projecthub-portal-muted);
	font-size: 0.8125rem;
	margin-top: 0.75rem;
}

.projecthub-portal-detail-list dt:first-child {
	margin-top: 0;
}

.projecthub-portal-detail-list dd {
	margin: 0.15rem 0 0;
}

.projecthub-portal-detail-summary {
	white-space: pre-line;
}

/* Replaces the former requirements/tasks/gantt/chat/files/activity CTA
 * button row (Project Detail Page Redesign Unit) — the sidebar's own
 * Project-scoped sub-nav is the primary way to reach those pages now. */
.projecthub-portal-detail-updates {
	margin-top: 1.5rem;
}

.projecthub-portal-detail-updates h2 {
	font-size: 1.125rem;
	margin: 0 0 1rem;
}

.projecthub-portal-version-summary {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	margin-bottom: 1.5rem;
	padding: 1.25rem;
}

.projecthub-portal-version-summary__number {
	color: var(--projecthub-portal-muted);
	font-size: 0.875rem;
	margin-left: 0.5rem;
}

.projecthub-portal-version-summary__approval {
	margin: 0.75rem 0 0;
}

.projecthub-portal-version-summary__approval dt {
	color: var(--projecthub-portal-muted);
	font-size: 0.8125rem;
	margin-top: 0.5rem;
}

.projecthub-portal-version-summary__approval dd {
	margin: 0.1rem 0 0;
}

.projecthub-portal-version-summary__note {
	color: var(--projecthub-portal-muted);
	margin: 0.75rem 0 0;
}

.projecthub-portal-requirement-sections {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	margin-top: 1.5rem;
}

.projecthub-portal-requirement-section {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	padding: 1.25rem;
}

.projecthub-portal-requirement-section__title {
	font-size: 1.0625rem;
	margin: 0 0 0.75rem;
}

.projecthub-portal-requirement-section__empty {
	color: var(--projecthub-portal-muted);
	margin: 0;
}

.projecthub-portal-requirement-section__text {
	margin: 0.5rem 0 0;
	white-space: pre-line;
}

.projecthub-portal-requirement-goals {
	margin: 0;
	padding-left: 1.25rem;
}

/*
 * Client Portal UI Restoration — Requirement dt/dd Visual Hierarchy: dt is
 * bold and colored in the design system's own primary blue
 * (--projecthub-portal-accent, the same token every portal link/focus ring
 * already uses — no new color introduced) so a field label reads as a
 * clear heading, not muted secondary text. Spacing is asymmetric by
 * design: dt→dd stays tight (margin-top: 0.2rem on dd) so a label and its
 * own value read as one visually-grouped pair, while dd→next-dt is wider
 * (margin-top: 1.1rem on dt) so consecutive label/value pairs stay
 * visually separated even in a long Requirement section — the dt:first-
 * child override keeps the very first pair flush against the section
 * top.
 */
.projecthub-portal-requirement-fields dt,
.projecthub-portal-requirement-item dt {
	color: var(--projecthub-portal-accent);
	font-size: 0.8125rem;
	font-weight: 700;
	margin-top: 1.1rem;
}

.projecthub-portal-requirement-fields dt:first-child,
.projecthub-portal-requirement-item dt:first-child {
	margin-top: 0;
}

/*
 * dd indents modestly relative to its own dt (~1em) and wraps as plain
 * text: white-space: normal (the explicit "no white-space override"
 * requirement) rather than the previous pre-line, which double-preserved
 * line breaks together with the PHP side's own nl2br(esc_html(...)) call —
 * nl2br() already inserts the real <br> tags a line break should render
 * as, so pre-line's own newline-preserving behavior on top of that was
 * redundant, not merely unnecessary.
 */
.projecthub-portal-requirement-fields dd,
.projecthub-portal-requirement-item dd {
	margin: 0.2rem 0 0 1em;
	white-space: normal;
}

.projecthub-portal-requirement-item {
	border-top: 1px solid var(--projecthub-portal-border);
	margin: 0;
	padding: 1.1rem 0 0;
}

.projecthub-portal-requirement-item:first-child {
	border-top: none;
	padding-top: 0;
}

.projecthub-portal-requirement-interaction {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-left: 3px solid var(--projecthub-portal-accent);
	border-radius: 8px;
	margin-bottom: 1.5rem;
	padding: 1rem 1.25rem;
}

.projecthub-portal-requirement-interaction__message {
	margin: 0;
}

.projecthub-portal-requirement-interaction__actions {
	color: var(--projecthub-portal-muted);
	font-size: 0.875rem;
	margin: 0.6rem 0 0;
	padding-left: 1.25rem;
}

.projecthub-portal-request-changes-form {
	margin-top: 0.4rem;
}

.projecthub-portal-approve-form {
	margin-top: 0.4rem;
	max-width: 28rem;
}

.projecthub-portal-approve-form label {
	display: block;
	font-size: 0.8125rem;
	margin-bottom: 0.3rem;
}

.projecthub-portal-approve-form__comment {
	box-sizing: border-box;
	font-family: inherit;
	font-size: 0.9375rem;
	margin-bottom: 0.5rem;
	min-height: 3.5rem;
	padding: 0.5rem;
	resize: vertical;
	width: 100%;
}

.projecthub-portal-requirement-notice {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-success);
	border-radius: 8px;
	color: var(--projecthub-portal-success);
	margin-bottom: 1.5rem;
	padding: 0.75rem 1rem;
}

.projecthub-portal-comment-error {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-danger);
	border-radius: 8px;
	color: var(--projecthub-portal-danger);
	margin-bottom: 1.5rem;
	padding: 0.75rem 1rem;
}

.projecthub-portal-comments {
	margin-top: 1rem;
}

.projecthub-portal-comments__empty {
	color: var(--projecthub-portal-muted);
	font-size: 0.875rem;
	margin: 0.5rem 0;
}

.projecthub-portal-comment {
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	margin-bottom: 0.75rem;
	padding: 0.75rem 1rem;
}

.projecthub-portal-comment__header {
	align-items: baseline;
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: 0.4rem;
}

.projecthub-portal-comment__author {
	font-weight: 600;
}

.projecthub-portal-comment__date {
	color: var(--projecthub-portal-muted);
	font-size: 0.8125rem;
}

.projecthub-portal-comment__body {
	margin: 0;
	white-space: pre-line;
}

.projecthub-portal-comment__resolved-at {
	color: var(--projecthub-portal-muted);
	font-size: 0.8125rem;
	margin: 0.4rem 0 0;
}

.projecthub-portal-comment__resolve {
	margin-top: 0.5rem;
}

.projecthub-portal-comment-form {
	margin-top: 0.75rem;
}

.projecthub-portal-comment-form__body {
	box-sizing: border-box;
	font-family: inherit;
	font-size: 0.9375rem;
	min-height: 4.5rem;
	padding: 0.5rem;
	resize: vertical;
	width: 100%;
}

.projecthub-portal-chat-notice {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-success);
	border-radius: 8px;
	color: var(--projecthub-portal-success);
	margin-bottom: 1.5rem;
	padding: 0.75rem 1rem;
}

.projecthub-portal-chat-error {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-danger);
	border-radius: 8px;
	color: var(--projecthub-portal-danger);
	margin-bottom: 1.5rem;
	padding: 0.75rem 1rem;
}

.projecthub-portal-chat-empty {
	color: var(--projecthub-portal-muted);
}

.projecthub-portal-chat-list {
	list-style: none;
	margin: 0 0 1rem;
	padding: 0;
}

/*
 * .is-own (Chat Visual Restoration) matches assets/css/admin-chat.css's own
 * .projecthub-chat-message.is-own values exactly, so a message reads the
 * same whether viewed from the Client Portal or the admin side.
 */
.projecthub-portal-chat-message {
	background: #ffffff;
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	margin-bottom: 0.75rem;
	margin-right: 7%;
	padding: 0.75rem 1rem;
}

.projecthub-portal-chat-message.is-own {
	background: #eaf2fb;
	border: 1px solid #a3c9ec;
	margin-left: 7%;
	margin-right: 0;
}

.projecthub-portal-chat-message__meta {
	align-items: baseline;
	color: var(--projecthub-portal-muted);
	display: flex;
	flex-wrap: wrap;
	font-size: 0.8125rem;
	gap: 0.5rem;
	margin: 0 0 0.4rem;
}

.projecthub-portal-chat-message__author {
	color: var(--projecthub-portal-text);
	font-weight: 600;
}

.projecthub-portal-chat-message__body {
	margin: 0;
	overflow-wrap: anywhere;
}

.projecthub-portal-chat-form {
	margin-top: 0.75rem;
}

.projecthub-portal-chat-form__body {
	box-sizing: border-box;
	font-family: inherit;
	font-size: 0.9375rem;
	min-height: 4.5rem;
	padding: 0.5rem;
	resize: vertical;
	width: 100%;
}

.projecthub-portal-chat-form__files {
	display: block;
	margin-top: 0.4rem;
	max-width: 100%;
}

.projecthub-portal-chat-message__attachments {
	list-style: none;
	margin: 0.5rem 0 0;
	padding: 0;
}

.projecthub-portal-chat-message__attachment {
	margin-top: 0.25rem;
}

.projecthub-portal-chat-message__attachment-link {
	overflow-wrap: anywhere;
}

.projecthub-portal-file-empty {
	color: var(--projecthub-portal-muted);
}

/*
 * Client Portal UI Restoration — Files grid: exactly 3 equal columns,
 * literally capped (not an auto-fit/minmax approximation, which would
 * grow past 3 on a wide-enough viewport) — the two @media steps below
 * (matching this file's own existing 768px/480px breakpoints, e.g.
 * .projecthub-portal-file's own padding step) drop this to 2 then 1
 * column as the viewport narrows, the same progressive-columns pattern as
 * every other multi-step responsive rule in this file.
 */
.projecthub-portal-file-list {
	display: grid;
	gap: 1rem;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	list-style: none;
	margin: 0 0 1rem;
	padding: 0;
}

.projecthub-portal-file {
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	padding: 0.75rem;
}

/*
 * The preview (thumbnail image or icon) and the filename share one click
 * target — a plain file-grid card convention, not a change to what
 * information is shown: the download URL itself, and the uploader/date/
 * size meta line below it, are exactly what list.php already rendered
 * before this Unit, just laid out as a grid of cards instead of a
 * stacked list.
 */
.projecthub-portal-file__preview-link {
	color: inherit;
	display: block;
	text-decoration: none;
}

/*
 * A fixed 1:1 box regardless of the source image's own aspect ratio:
 * object-fit: cover crops visually at display time so every card reads as
 * a uniform grid tile, rather than PortalFileStorageService generating a
 * server-side cropped derivative (see that class's own
 * ensureThumbnailCached() docblock for why the crop was deliberately left
 * to CSS instead).
 */
.projecthub-portal-file__preview {
	align-items: center;
	aspect-ratio: 1 / 1;
	background: var(--projecthub-portal-bg);
	border-radius: 6px;
	display: flex;
	justify-content: center;
	margin-bottom: 0.5rem;
	overflow: hidden;
}

.projecthub-portal-file__thumbnail {
	height: 100%;
	object-fit: cover;
	width: 100%;
}

/* The generic-file fallback icon (any non-image, or an image the Project
	has no thumbnail route context for) — sized relative to its own
	.projecthub-portal-file__preview box rather than a fixed px value, so
	it scales consistently if that box's own size ever changes. */
.projecthub-portal-file__icon {
	color: var(--projecthub-portal-muted);
	height: 40%;
	width: 40%;
}

.projecthub-portal-file__meta {
	align-items: baseline;
	color: var(--projecthub-portal-muted);
	display: flex;
	flex-wrap: wrap;
	font-size: 0.8125rem;
	gap: 0.5rem;
	margin: 0 0 0.4rem;
}

.projecthub-portal-file__uploader {
	color: var(--projecthub-portal-text);
	font-weight: 600;
}

.projecthub-portal-file__name {
	font-weight: 600;
	margin: 0 0 0.4rem;
	overflow-wrap: anywhere;
}

.projecthub-portal-activity-empty {
	color: var(--projecthub-portal-muted);
}

.projecthub-portal-activity-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.projecthub-portal-activity-item {
	border: 1px solid var(--projecthub-portal-border);
	border-left: 3px solid var(--projecthub-portal-border);
	border-radius: 8px;
	margin-bottom: 0.75rem;
	padding: 0.75rem 1rem;
}

.projecthub-portal-activity-item--approved {
	border-left-color: var(--projecthub-portal-success);
}

.projecthub-portal-activity-item--changes-requested {
	border-left-color: var(--projecthub-portal-danger);
}

.projecthub-portal-activity-item--review-requested,
.projecthub-portal-activity-item--comment-posted,
.projecthub-portal-activity-item--comment-resolved {
	border-left-color: var(--projecthub-portal-accent);
}

.projecthub-portal-activity-item__summary {
	margin: 0 0 0.4rem;
	overflow-wrap: anywhere;
}

.projecthub-portal-activity-item__meta {
	color: var(--projecthub-portal-muted);
	display: flex;
	flex-wrap: wrap;
	font-size: 0.8125rem;
	gap: 0.5rem;
	margin: 0;
}

.projecthub-portal-pagination {
	align-items: center;
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
	margin-top: 1rem;
}

.projecthub-portal-pagination__status {
	color: var(--projecthub-portal-muted);
	font-size: 0.8125rem;
}

/* -- dashboard (Phase 43 Unit D) -----------------------------------------
 * Card/list layout throughout, deliberately not a <table>: the Project
 * list already owns .projecthub-portal-table-scroll for its own wide,
 * many-column view, and the Dashboard's own "進行中案件" widget is a
 * narrower summary that reads better as cards, never introducing a second
 * horizontally-scrolling region on this page.
 */

.projecthub-portal-dashboard {
	display: flex;
	flex-direction: column;
	gap: 2rem;
}

.projecthub-portal-dashboard-summary {
	display: grid;
	gap: 1rem;
	grid-template-columns: repeat(2, minmax(0, 1fr));
}

.projecthub-portal-dashboard-card {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	padding: 1.25rem;
	text-align: center;
}

.projecthub-portal-dashboard-card__value {
	color: var(--projecthub-portal-accent);
	font-size: 2rem;
	font-weight: 600;
	margin: 0;
}

.projecthub-portal-dashboard-card__label {
	color: var(--projecthub-portal-muted);
	margin: 0.25rem 0 0;
}

.projecthub-portal-dashboard-attention h2,
.projecthub-portal-dashboard-projects h2,
.projecthub-portal-dashboard-activity h2 {
	font-size: 1.125rem;
	margin: 0 0 1rem;
}

.projecthub-portal-dashboard-empty {
	color: var(--projecthub-portal-muted);
}

.projecthub-portal-dashboard-attention__list,
.projecthub-portal-dashboard-projects__list {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.projecthub-portal-dashboard-attention__item,
.projecthub-portal-dashboard-project-card {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	padding: 1.25rem;
}

.projecthub-portal-dashboard-attention__link,
.projecthub-portal-dashboard-project-card__link {
	display: inline-block;
	font-weight: 600;
	margin-bottom: 0.5rem;
}

.projecthub-portal-dashboard-attention__action {
	margin: 0.5rem 0 0;
}

.projecthub-portal-dashboard-attention__meta,
.projecthub-portal-dashboard-project-card__meta {
	color: var(--projecthub-portal-muted);
	font-size: 0.8125rem;
	margin: 0.35rem 0 0;
}

/* -- project list (Phase 43 Unit E) ---------------------------------------
 * Card/list layout, deliberately not a <table>: this Unit adds
 * Requirement-status/要対応/last-update to what the list already showed,
 * and a table wide enough for every column would force horizontal
 * scrolling on every viewport, not just narrow ones. Mirrors the
 * Dashboard project card's own visual language (surface/border/radius)
 * without reusing its classes directly, since the two screens' fields
 * differ.
 */

.projecthub-portal-project-list {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.projecthub-portal-project-list-card {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	padding: 1.25rem;
}

.projecthub-portal-project-list-card__link {
	display: inline-block;
	font-weight: 600;
	margin-bottom: 0.5rem;
}

.projecthub-portal-project-list-card__badges {
	margin: 0 0 0.5rem;
}

.projecthub-portal-project-list-card__badges .projecthub-portal-badge {
	margin-right: 0.35rem;
}

.projecthub-portal-project-list-card__meta {
	color: var(--projecthub-portal-muted);
	font-size: 0.8125rem;
	margin: 0.35rem 0 0;
}

.projecthub-portal-project-list-card__detail-link {
	display: inline-block;
	margin-top: 0.75rem;
}

/* -- task list (Phase 44 Unit G) -------------------------------------------
 * Card/list layout, mirroring .projecthub-portal-project-list's own visual
 * language (surface/border/radius, badge, meta text) rather than a
 * <table>: consistent with this Unit's own instruction to reuse existing
 * Portal card patterns instead of introducing a new layout system.
 */

.projecthub-portal-task-list {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.projecthub-portal-task-list-card {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-border);
	border-radius: 8px;
	padding: 1.25rem;
}

.projecthub-portal-task-list-card__name {
	font-weight: 600;
	margin: 0 0 0.5rem;
}

/* Three-Level Task Hierarchy Unit E: only the Task name text indents to
	show visible-only hierarchy depth — every other field on the card stays
	unindented. Slightly smaller steps than the Admin equivalent
	(.projecthub-task-depth-1/2 in admin.css, 20px/40px), per docs/27 §58's
	own allowance; depth is always a small server-computed integer (0/1/2)
	reflecting the Portal's own reconstructed visible-only hierarchy, never
	the Admin hierarchy's raw depth. */
.projecthub-portal-task-depth-1 .projecthub-portal-task-list-card__name {
	padding-inline-start: 16px;
}

.projecthub-portal-task-depth-2 .projecthub-portal-task-list-card__name {
	padding-inline-start: 32px;
}

.projecthub-portal-task-list-card__badges {
	margin: 0 0 0.5rem;
}

.projecthub-portal-task-list-card__meta {
	color: var(--projecthub-portal-muted);
	font-size: 0.8125rem;
	margin: 0.35rem 0 0;
}

.projecthub-portal-task-status-form {
	align-items: center;
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 0.75rem;
}

.projecthub-portal-task-status-form label {
	font-size: 0.8125rem;
}

.projecthub-portal-task-notice {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-success);
	border-radius: 8px;
	color: var(--projecthub-portal-success);
	margin-bottom: 1.5rem;
	padding: 0.75rem 1rem;
}

.projecthub-portal-task-error {
	background: var(--projecthub-portal-surface);
	border: 1px solid var(--projecthub-portal-danger);
	border-radius: 8px;
	color: var(--projecthub-portal-danger);
	margin-bottom: 1.5rem;
	padding: 0.75rem 1rem;
}

/* -- gantt chart (Phase 45 Unit D, read-only; daily grid header added by
 *    Client Portal Daily Grid Gantt Unit B) --------------------------------
 * A single CSS Grid spans the whole chart (never one grid per row): column 1
 * is the fixed-width Task info cell, and the remaining --gantt-day-count
 * columns are the day track every Task bar positions itself within via an
 * inline grid-row/grid-column set from PortalGanttTaskDisplayView's own
 * offsetDays()/spanDays() (Unit C's timeline calculation, never
 * recalculated here). --gantt-day-count and every grid-row/grid-column
 * value are plain integers from that calculation, never a user string
 * (docs/20-phase-45-gantt-plan.md §30-33).
 *
 * .projecthub-portal-gantt-row uses display: contents so each <li> stays a
 * real list item for assistive technology while its own children (info
 * cell, bar or "period unset" cell) become direct grid participants of the
 * parent <ul> — letting a semantic list and a single shared day-column grid
 * coexist without one compromising the other.
 *
 * Header rows (month/day-number/weekday, grid-row 1-3) live inside this
 * exact same grid, sharing the identical column definition every bar
 * already positions against — this is what structurally guarantees
 * header/bar column alignment, not a second, separately-sized header grid
 * kept in sync by convention (docs/26-client-portal-daily-grid-gantt-plan.md
 * §18). Weekend/today day-column shading is one background <li> per such
 * day, spanning every Task row vertically (grid-row: N / span row-count) —
 * O(days) total, never O(tasks x days) — rather than a
 * repeating-linear-gradient, because the day columns are flexible
 * (minmax(28px, 1fr)) and a fixed-pixel gradient cannot stay phase-aligned
 * with a variable-width, variable-start-weekday column track (docs/26 §24).
 * --projecthub-portal-gantt-weekend-bg (Client Portal Daily Grid Gantt
 * Unit D-Fix, docs/26 §41.13/§43) reuses the existing
 * --projecthub-portal-border token rather than --projecthub-portal-bg:
 * this Gantt section renders directly on body.projecthub-portal's own
 * background (no intervening white --projecthub-portal-surface card),
 * so reusing --projecthub-portal-bg made weekend cells exactly the same
 * colour as the page behind every non-weekend cell — invisible in
 * practice, confirmed in a real browser. --projecthub-portal-border is
 * already the design system's own "subtle but visible against the page
 * background" gray, reused here rather than introducing a new hex value.
 * .projecthub-portal-gantt-row__info and .projecthub-portal-gantt-header-spacer
 * (both column 1) are sticky so the Task info column and the header's own
 * column-1 gutter stay pinned during horizontal scroll (docs/26 §33);
 * sticky *header rows* (vertical stickiness) are explicitly out of scope
 * for V1 (docs/26 §10 Q4, deferred).
 *
 * .projecthub-portal-gantt-range-too-long (Client Portal Daily Grid Gantt
 * Unit C-Fix) is the fallback text shown instead of the entire grid/bar
 * block (never a partial or truncated grid) when
 * PortalGanttView::isTimelineTooLong() is true — this --gantt-day-count
 * grid-template-columns mechanism is itself unbounded by Task date range
 * width (Phase 45, pre-dating the day-grid), so the whole block is
 * skipped, not only the Unit B day-grid header (docs/26 §16/§39).
 *
 * contain: layout (Client Portal Daily Grid Gantt Unit D-Fix, docs/26
 * §41.21/§43) works around a real browser rendering limitation, not a
 * source misconfiguration: without it, position: sticky on a direct CSS
 * Grid item (.projecthub-portal-gantt-row__info /
 * -header-spacer, both grid-column: 1) only holds correctly for a
 * horizontal scroll distance up to (scroll-container clientWidth − the
 * sticky element's own width) — confirmed empirically across a wide
 * range of scroll offsets — then drifts linearly with the scroll amount
 * as if it had silently become position: static. contain: layout on
 * this grid makes the browser recompute the sticky element's
 * containing/constraint rect against the grid's own (correct, full)
 * box instead, closing the gap entirely at every tested scroll
 * position. contain: layout alone also makes the grid stop
 * contributing its true content width to .projecthub-portal-gantt-timeline-scroll's
 * scrollable area (verified: scrollWidth collapses to clientWidth,
 * killing horizontal scroll outright) — width: max-content restores
 * the grid's intrinsic, content-driven width so scrolling keeps working
 * exactly as before; the pre-existing min-width: 100% below still
 * floors it at the container's full width for short ranges too narrow
 * to need scrolling, since min-width always wins over a smaller width
 * value in the CSS box model.
 */

.projecthub-portal-gantt-empty {
	color: var(--projecthub-portal-muted);
}

.projecthub-portal-gantt-range {
	color: var(--projecthub-portal-muted);
	font-size: 0.875rem;
	margin: 0 0 1rem;
}

.projecthub-portal-gantt-range-too-long {
	color: var(--projecthub-portal-muted);
}

/*
 * Client Portal UI Restoration — Sticky Date Header: mirrors the reasoning
 * WP Admin's own Sticky Date Header fix already established
 * (assets/css/admin-gantt.css's own .projecthub-admin-gantt-timeline-scroll
 * docblock) — a position: sticky descendant only ever sticks relative to
 * its nearest ancestor that is itself an actual, bounded scroll container.
 * This element's existing horizontal-overflow rule alone already makes its
 * computed vertical overflow resolve to auto too (a plain CSS Overflow
 * Module rule), but with no bounded height its own content never overflows
 * vertically, so nothing on that axis ever had anywhere to "stick" to —
 * confirmed directly via
 * Browser QA that a naive position: sticky on the header rows below did
 * nothing at all without this. The Portal page has no wp-admin bar (this is
 * a standalone document, templates/portal/layout.php), so the max-height
 * formula only needs to subtract the fixed portal header
 * (--projecthub-portal-header-height) and a constant covering the page
 * title/back-link/range text above this element — measured directly
 * against the real rendered page, not copied from WP Admin's own unrelated
 * chrome.
 */
.projecthub-portal-gantt-timeline-scroll {
	max-height: calc(100vh - var(--projecthub-portal-header-height) - 220px);
	max-width: 100%;
	min-height: 240px;
	overflow-x: auto;
	overflow-y: auto;
	padding-bottom: 0.5rem;
}

.projecthub-portal-gantt {
	column-gap: 1rem;
	contain: layout;
	display: grid;
	grid-template-columns: minmax(200px, 260px) repeat(var(--gantt-day-count, 1), minmax(28px, 1fr));
	list-style: none;
	margin: 0;
	min-width: 100%;
	padding: 0;
	/* Hierarchy UI Redesign (revised, Client Portal UI Restoration): 0, not
		a small nonzero gap — every row (header and body alike) must render
		perfectly flush against the next with nothing in between, since
		within a hierarchy group (a root Task plus its visible children/
		grandchildren) the info cells now share one continuous border as a
		single unbroken block (.projecthub-portal-gantt-row__info--group-start/
		--group-end below); any row-gap here would show as a seam splitting
		that block. Spacing between two different root Tasks' groups comes
		instead from a real .projecthub-portal-gantt-group-spacer grid row
		templates/portal/gantt/list.php inserts before each new group
		(except the first) — its own explicit height, not a gap value, so
		it applies uniformly across every column without relying on row-gap
		at all. */
	row-gap: 0;
	width: max-content;
}

/* Client Portal UI Restoration: the visible space between two different
	root Tasks' groups (docs comment on .projecthub-portal-gantt above).
	Spans every column (not just the info column) so the gap reads as one
	full-width band, and carries no background so day-column weekend/today
	shading (a lower, non-sticky layer) still shows through it uninterrupted,
	matching the shading's own continuous look through an ordinary
	within-group row boundary. */
.projecthub-portal-gantt-group-spacer {
	grid-column: 1 / -1;
	height: 0.75rem;
}

.projecthub-portal-gantt-row {
	display: contents;
}

/* Hierarchy UI Redesign: a collapsed row (JS-toggled, .is-collapsed) is
	fully removed from the grid — its display: none overrides this rule's
	own display: contents, taking its info/bar/unscheduled children out of
	layout entirely rather than leaving an empty grid row. */
.projecthub-portal-gantt-row.is-collapsed {
	display: none;
}

/*
 * Client Portal UI Restoration: a hierarchy group (a root Task plus its
 * visible children/grandchildren, list.php's own $is_group_start/
 * $is_group_end) now reads as one unbroken block — left/right borders on
 * every row so the sides form a continuous line down the whole group
 * (row-gap: 0 above means adjacent rows touch exactly, so these can never
 * show a seam), while the top/bottom border and rounded corners apply only
 * at the group's own start/end row respectively. A lone root Task with no
 * children carries both modifier classes and so renders a complete
 * 4-sided card, same as before this Unit.
 */
.projecthub-portal-gantt-row__info {
	background: var(--projecthub-portal-surface);
	border-left: 1px solid var(--projecthub-portal-border);
	border-right: 1px solid var(--projecthub-portal-border);
	grid-column: 1;
	left: 0;
	padding: 0.4rem 0.75rem;
	position: sticky;
	z-index: 2;
}

.projecthub-portal-gantt-row__info--group-start {
	border-top: 1px solid var(--projecthub-portal-border);
	border-top-left-radius: 8px;
	border-top-right-radius: 8px;
}

.projecthub-portal-gantt-row__info--group-end {
	border-bottom: 1px solid var(--projecthub-portal-border);
	border-bottom-left-radius: 8px;
	border-bottom-right-radius: 8px;
}

/*
 * Client Portal UI Restoration — hierarchy indent root cause fix: the
 * previous approach stepped the *sticky* .projecthub-portal-gantt-row__info
 * box itself right via margin-left, which a real-browser Horizontal Scroll
 * QA confirmed collapses back to the unindented position once the timeline
 * is scrolled — position: sticky's own "stuck" offset is computed from this
 * element's inset (left: 0) against its scroll container, and margin on the
 * sticky element itself is not reliably honored by every engine once it is
 * actually in its stuck state. WP Admin's own Gantt (assets/css/
 * admin-gantt.css .projecthub-admin-gantt-depth-1/2
 * .projecthub-admin-gantt-row__info-box) never had this bug because it
 * indents via padding-inline-start on a plain (non-sticky) inner box
 * instead — the sticky element's own box never moves, so there is nothing
 * for scroll to un-stick. This mirrors that pattern: .projecthub-portal-
 * gantt-row__info-box is a plain grid-item child of the sticky info cell
 * above it, and depth indentation is padding-inline-start on that inner
 * box, which survives horizontal scroll unconditionally because it never
 * depends on position: sticky's own offset math at all.
 */
.projecthub-portal-gantt-row__info-box {
	position: relative;
}

.projecthub-portal-task-depth-1 .projecthub-portal-gantt-row__info-box {
	padding-inline-start: 20px;
}

.projecthub-portal-task-depth-2 .projecthub-portal-gantt-row__info-box {
	padding-inline-start: 40px;
}

/* The "接続線": drawn in the padding-inline-start gutter the rules above
	just opened up, not flush against the content's own left edge — spans
	the full row height so consecutive same-depth rows visually chain into
	one continuous trunk line. Positioned against .projecthub-portal-gantt-
	row__info-box (a plain, non-sticky box), so it moves correctly with the
	indented content during horizontal scroll instead of drifting the way a
	line anchored to the sticky parent's own offset would. */
.projecthub-portal-task-depth-1 .projecthub-portal-gantt-row__info-box::before,
.projecthub-portal-task-depth-2 .projecthub-portal-gantt-row__info-box::before {
	border-left: 2px solid var(--projecthub-portal-border);
	bottom: 0;
	content: '';
	position: absolute;
	top: 0;
}

.projecthub-portal-task-depth-1 .projecthub-portal-gantt-row__info-box::before {
	left: 10px;
}

.projecthub-portal-task-depth-2 .projecthub-portal-gantt-row__info-box::before {
	left: 30px;
}

.projecthub-portal-gantt-row__name {
	align-items: center;
	display: flex;
	font-weight: 600;
	gap: 0.4rem;
	margin: 0 0 0.15rem;
}

.projecthub-portal-gantt-row__name-text {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.projecthub-portal-gantt-row__assignee {
	color: var(--projecthub-portal-muted);
	flex-shrink: 0;
	font-size: 0.75rem;
	font-weight: 400;
}

/* Hierarchy UI Redesign: the collapse/expand toggle, rendered only for a
	row with at least one visible child (PortalGanttTaskDisplayView::
	hasChildren()). A plain button, no confirm()/dialog — assets/js/
	portal-gantt.js toggles .is-collapsed on descendant rows and flips
	aria-expanded; nothing here depends on JS having run (the button is
	inert, always expanded, until portal-gantt.js attaches its handler). */
.projecthub-portal-gantt-toggle {
	background: none;
	border: none;
	border-radius: 4px;
	color: var(--projecthub-portal-muted);
	cursor: pointer;
	display: inline-flex;
	flex-shrink: 0;
	padding: 2px;
}

.projecthub-portal-gantt-toggle:hover,
.projecthub-portal-gantt-toggle:focus-visible {
	background: var(--projecthub-portal-gantt-weekend-bg);
	color: var(--projecthub-portal-accent);
}

.projecthub-portal-gantt-toggle-icon {
	border-bottom: 4px solid currentColor;
	border-left: 4px solid transparent;
	border-right: 4px solid transparent;
	display: block;
	height: 0;
	transform: rotate(180deg);
	transition: transform 0.15s ease;
	width: 0;
}

.projecthub-portal-gantt-toggle[aria-expanded='false'] .projecthub-portal-gantt-toggle-icon {
	transform: rotate(90deg);
}

/*
 * Client Portal UI Restoration — Task Row Height: status and assignee now
 * share one line (align-items: center flex row) instead of stacking as two
 * separate <p> elements, shrinking the single-line-common-case row height
 * without touching task name readability, hierarchy indentation, or the
 * click target (this line has no interactive control of its own).
 */
.projecthub-portal-gantt-row__meta {
	align-items: center;
	color: var(--projecthub-portal-muted);
	display: flex;
	font-size: 0.75rem;
	gap: 0.4rem;
	margin: 0.1rem 0 0;
}

/*
 * Client Portal UI Restoration — Status Visual: the status badge inside a
 * Gantt row reuses admin.css's own .projecthub-badge--status-* color
 * mapping (via BadgePresenter::forTaskStatus(), never a new status color
 * system — PortalGanttTaskViewFactory resolves the variant, this selector
 * only renders it), scoped to a smaller size than both the shared
 * .projecthub-portal-badge default and WP Admin's own 12px badge, per this
 * row's own compact density requirement — every other .projecthub-portal-
 * badge usage (Requirement page, etc.) is untouched.
 */
.projecthub-portal-gantt-row__meta .projecthub-portal-badge {
	flex-shrink: 0;
	font-size: 0.6875rem;
	font-weight: 600;
	padding: 0.05rem 0.5rem;
}

.projecthub-portal-badge--status-not-started {
	background: #f0f0f1;
	border-color: #c3c4c7;
	color: #3c434a;
}

.projecthub-portal-badge--status-in-progress {
	background: #d4f4dd;
	border-color: transparent;
	color: #1a4d2e;
}

.projecthub-portal-badge--status-waiting-review,
.projecthub-portal-badge--status-on-hold {
	background: #fef3cd;
	border-color: transparent;
	color: #7a5c00;
}

.projecthub-portal-badge--status-completed {
	background: #dde7f5;
	border-color: transparent;
	color: #1d3a5f;
}

.projecthub-portal-gantt-bar {
	align-self: center;
	background: var(--projecthub-portal-accent);
	border-radius: 4px;
	height: 1rem;
	position: relative;
	z-index: 1;
}

/* Hierarchy UI Redesign: a slightly shorter bar per depth level, still
	vertically centered in its own row — a purely visual cue reinforcing
	that a child/grandchild's period sits nested within its ancestors',
	alongside the row stacking itself. The actual containment guarantee
	comes from TaskAncestorScheduleAdjuster (Application layer), not from
	this styling. */
.projecthub-portal-gantt-bar--depth-1 {
	height: 0.75rem;
}

.projecthub-portal-gantt-bar--depth-2 {
	height: 0.5rem;
}

/* Hierarchy UI Redesign: a completed Task (TaskStatus::COMPLETED) reads
	as visually "done" — a light gray box background, dimmed name text,
	and a gray bar, regardless of depth. --projecthub-portal-bg is this
	design system's own existing light gray (the page's own background,
	reused here rather than introducing a new hex value). */
.projecthub-portal-task-completed .projecthub-portal-gantt-row__info {
	background: var(--projecthub-portal-bg);
}

.projecthub-portal-task-completed .projecthub-portal-gantt-row__name-text {
	color: var(--projecthub-portal-muted);
}

.projecthub-portal-task-completed .projecthub-portal-gantt-row__name-text::before {
	content: '\2713\0020';
}

.projecthub-portal-task-completed .projecthub-portal-gantt-bar {
	background: var(--projecthub-portal-muted);
}

.projecthub-portal-gantt-row__unscheduled {
	align-self: center;
	color: var(--projecthub-portal-muted);
	font-size: 0.75rem;
	grid-column: 2 / -1;
	margin: 0;
}

/*
 * Client Portal UI Restoration — Sticky Date Header (top-left corner):
 * z-index: 5 stays the highest of the header's own stacking group
 * (month/day-number/weekday below sit at 4) so this corner cell still wins
 * over both a day header cell and a Task row passing beneath it during
 * horizontal/vertical scroll. background is transparent so a Task row (or
 * the group-spacer band) scrolling underneath during vertical scroll shows
 * through rather than being masked by an opaque corner box. top: 0 needs
 * no per-row offset since this single cell already spans all 3 header row
 * tracks (grid-row: 1 / span 3, set by the PHP template), and with row-gap:
 * 0 on the parent grid (see .projecthub-portal-gantt above) its rendered
 * height already equals the header rows' own combined height exactly, with
 * nothing left to reconcile.
 */
.projecthub-portal-gantt-header-spacer {
	background: transparent;
	grid-column: 1;
	left: 0;
	position: sticky;
	top: 0;
	z-index: 5;
}

/*
 * Client Portal UI Restoration — Sticky Date Header backdrop (calendar
 * side): month/day-number/weekday below (grid-row 1/2/3) are each placed
 * in exactly one day column (or, for month, a span across same-month day
 * columns) — none of them extends into this grid's own column-gap: 1rem
 * track between adjacent day columns, since a plain, non-spanning grid
 * item's box never includes the gap next to it. With nothing else opaque
 * covering those 1rem gaps, a Task row/bar scrolling underneath during
 * vertical scroll was visible through them, right through the sticky
 * header's own 3-row band — reported directly against the real rendered
 * page. This single element, spanning every calendar column and all 3
 * header rows (grid-row: 1 / span 3, grid-column: 2 / -1, set by the PHP
 * template) the same way .projecthub-portal-gantt-header-spacer already
 * spans column 1, gets an opaque background specifically because its whole
 * reason to exist is covering those gap tracks — unlike the corner spacer
 * above, which stays transparent per this Unit's own explicit instruction.
 * z-index: 3 sits below the header text cells themselves (4) and the
 * corner spacer (5), so it never covers the header's own date labels, but
 * above every body-row element that can scroll under it (info: 2, bar: 1,
 * day-column background: 0).
 */
.projecthub-portal-gantt-header-backdrop {
	background: var(--projecthub-portal-surface);
	position: sticky;
	top: 0;
	z-index: 3;
}

/*
 * Sticky Date Header: all 3 rows are independently position: sticky (a
 * single element cannot span 3 separately-offset sticky positions), each
 * needing its own cumulative `top` equal to the row height(s) above it.
 * month and day-number get an explicit `height` (rather than relying on
 * content/line-height/border to render exactly 1.35rem) so this arithmetic
 * is exact regardless of font metrics — box-sizing: border-box (global)
 * means each row's border/padding still renders inside that height, and
 * the existing flex + align-items: center still centers its text within
 * whatever height results. With row-gap: 0 on the parent grid, each row's
 * natural (unstuck) resting position already equals these same cumulative
 * offsets, so the header looks identical at rest and once stuck — no
 * separate gap-cancelling margin is needed here.
 */
.projecthub-portal-gantt-month,
.projecthub-portal-gantt-day-number,
.projecthub-portal-gantt-weekday {
	align-items: center;
	background: var(--projecthub-portal-surface);
	color: var(--projecthub-portal-muted);
	display: flex;
	font-size: 0.75rem;
	justify-content: center;
	padding: 0.2rem 0;
	position: sticky;
	z-index: 4;
}

.projecthub-portal-gantt-month {
	border-bottom: 1px solid var(--projecthub-portal-border);
	font-weight: 600;
	height: 1.35rem;
	top: 0;
}

.projecthub-portal-gantt-day-number {
	height: 1.35rem;
	top: 1.35rem;
}

.projecthub-portal-gantt-weekday {
	border-bottom: 1px solid var(--projecthub-portal-border);
	margin-bottom: 0.25rem;
	top: 2.7rem;
	padding-bottom: 0.4rem;
}

.projecthub-portal-gantt-day-number--weekend,
.projecthub-portal-gantt-weekday--weekend {
	background: var(--projecthub-portal-gantt-weekend-bg);
}

.projecthub-portal-gantt-day-number--today,
.projecthub-portal-gantt-weekday--today {
	background: var(--projecthub-portal-gantt-today-bg);
	color: var(--projecthub-portal-accent);
	font-weight: 600;
}

.projecthub-portal-gantt-day-column {
	align-self: stretch;
	z-index: 0;
}

.projecthub-portal-gantt-day-column--weekend {
	background: var(--projecthub-portal-gantt-weekend-bg);
}

.projecthub-portal-gantt-day-column--today {
	background: var(--projecthub-portal-gantt-today-bg);
}

/* -- responsive ------------------------------------------------------------
 * Desktop (> 960px): sidebar + main, side by side.
 * Tablet/mobile (<= 960px): navigation becomes a horizontal bar under the
 * header rather than a fixed-width side column — with a single visible
 * item today this stays a slim strip; it grows into a horizontally
 * readable bar as more items are added later, still without any JS. No
 * layout below relies on horizontal scrolling to avoid breaking; only the
 * navigation bar, .projecthub-portal-table-scroll (the Requirement
 * Version history table's own wrapper), and
 * .projecthub-portal-gantt-timeline-scroll (the Gantt chart's own
 * wrapper, Phase 45 Unit D) ever scroll horizontally, each contained to
 * that one element, never the page itself (Phase 42 Unit H §37 —
 * 320/375/768/1024 widths considered below).
 */

@media screen and (max-width: 960px) {
	.projecthub-portal__body {
		flex-direction: column;
	}

	.projecthub-portal-nav {
		border-bottom-right-radius: 0;
		border-right: none;
		width: 100%;
	}

	.projecthub-portal-nav__list {
		display: flex;
		overflow-x: auto;
		padding: 0.5rem;
	}

	.projecthub-portal-nav__link.is-active {
		border-left: none;
		border-bottom: 3px solid var(--projecthub-portal-accent);
		padding-left: 1.25rem;
	}

	.projecthub-portal__main {
		padding: 1rem;
	}
}

@media screen and (max-width: 768px) {
	.projecthub-portal-detail-card,
	.projecthub-portal-version-summary,
	.projecthub-portal-requirement-section,
	.projecthub-portal-activity-item,
	.projecthub-portal-dashboard-card,
	.projecthub-portal-dashboard-attention__item,
	.projecthub-portal-dashboard-project-card,
	.projecthub-portal-project-list-card,
	.projecthub-portal-task-list-card,
	.projecthub-portal-chat-message,
	.projecthub-portal-file {
		padding: 1rem;
	}

	.projecthub-portal-dashboard-summary {
		grid-template-columns: minmax(0, 1fr);
	}

	.projecthub-portal-file-list {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	/* Hierarchy UI Redesign: .projecthub-portal-gantt-row__info is
		deliberately excluded from the shared 1rem padding rule above —
		the Gantt's own compact row density (§ above the gantt chart
		rules) is a requirement at every width, not only desktop. */
	.projecthub-portal-gantt-row__info {
		padding: 0.5rem 0.625rem;
	}
}

@media screen and (max-width: 480px) {
	.projecthub-portal-login__container,
	.projecthub-portal-message__container {
		margin: 5vh 1rem;
		max-width: none;
		padding: 1.5rem;
	}

	.projecthub-portal__header {
		padding: 0.5rem;
	}

	.projecthub-portal-detail-card,
	.projecthub-portal-version-summary,
	.projecthub-portal-requirement-section,
	.projecthub-portal-activity-item,
	.projecthub-portal-chat-message,
	.projecthub-portal-file {
		padding: 0.75rem;
	}

	.projecthub-portal-file-list {
		grid-template-columns: minmax(0, 1fr);
	}
}
