/*
 * Fair Portal - layout + a small baseline skin. Cleaner rebuild (not a 1:1 port of
 * the legacy theme). The plugin owns the portal STRUCTURE; brand fonts/colours/buttons
 * still belong in the theme and can override the few baseline colours below, which are
 * exposed as --fm-portal-* custom properties so the theme can retheme without fighting
 * specificity.
 *
 * Design tokens (colour/type/spacing) come from shared/tokens.css (--fm-*); the
 * busy spinner lives in shared/components.css. Breakpoint: 768px (tablet+).
 * Mobile-first.
 */

/* Tunable baseline tokens (override in the theme). These are the THEME'S API and
   sit a layer above the shared --fm-* tokens, which only seed their defaults. */
#content {
	--fm-portal-sidebar-bg: #d7d9d8;
	--fm-portal-sidebar-fg: #000000;
	--fm-portal-sidebar-muted: #000000;
	--fm-portal-sidebar-hover: rgba( 0, 0, 0, 0.10 );
	--fm-portal-sidebar-active-bg: var( --fm-color-accent );
	--fm-portal-sidebar-active-fg: var( --fm-color-ink );
	--fm-portal-sidebar-width: 300px;
	--fm-portal-heading: #000000;
	--fm-portal-content-bg: #ffffff;
	/* The page-title panel when a page has no featured image. */
	--fm-portal-hero-bg: #2a2a2a;
	--fm-portal-hero-fg: #ffffff;
	/* Floor height for the portal frame, so a short page still fills a sensible
	 * amount of the screen. NOT 100vh: the theme puts a masthead above and a
	 * footer below, so a full viewport here guarantees a scrollbar on every
	 * page no matter how little content it holds. */
	--fm-portal-min-height: 80vh;
}

#content,
#content *,
#content *::before,
#content *::after {
	box-sizing: border-box;
}

/* ---- Page shell -------------------------------------------------------------- */

#content {
	max-width: 100%;
	margin: 0 auto;
	padding: 0;
	/* The shell sets the body face once; every module inherits it. Headings and
	   accents opt out per-selector below. */
	font-family: var( --fm-font-body );
	min-height: var( --fm-portal-min-height );
	/* The wrapper carries the same radius and clips to it. Without this its own
	   ground shows as four white slivers outside the rounded panel inside it. */
	border-radius: var( --fm-radius );
	overflow: hidden;
}

.page-content {
	display: grid;
	border-radius: var( --fm-radius );
	overflow: hidden;
	background-color: var( --fm-portal-content-bg, #ffffff );
	/* Soft, even ~50px shadow to give the portal a defined edge. */
	box-shadow: 0 0 50px rgba( 0, 0, 0, 0.08 );
	min-height: var( --fm-portal-min-height );
}


@media only screen and (min-width: 768px) {
	/* Logged-in: fixed sidebar column + fluid content, equal height (grid stretch). */
	.logged-in .page-content {
		grid-template-columns: var( --fm-portal-sidebar-width ) 1fr;
		align-items: stretch;
	}

	/* The public Fair Split layout has no sidebar, so it opts out of the two-column
	   logged-in grid and stays a single full-width cell. Its .login-page does the
	   50/50 split itself. Without this, a logged-in viewer squashes the content into
	   the 300px sidebar column. */
	.logged-in .page-content--solo {
		grid-template-columns: 1fr;
	}
}

/* ---- Sidebar ----------------------------------------------------------------- *
 * Mobile: a fixed slide-in drawer. Desktop: a static full-height column (it is the
 * first grid item, so grid stretch makes it match the content height). Either way a
 * flex column with the logout pinned to the bottom.
 */
.sidebar {
	display: flex;
	flex-direction: column;
	background-color: var( --fm-portal-sidebar-bg );
	color: var( --fm-portal-sidebar-fg );

	position: fixed;
	top: 0;
	left: 0;
	z-index: 9999;
	width: min( var( --fm-portal-sidebar-width ), 85vw );
	height: 100vh;
	overflow-y: auto;
	transform: translateX( -100% );
	transition: transform 0.3s ease;
	/* As a grid item on desktop, min-width:auto would let a long name blow the
	 * column past 300px (no truncation). Pin it to 0 so text-overflow can clamp. */
	min-width: 0;
}

.sidebar.opened {
	transform: translateX( 0 );
}

@media only screen and (min-width: 768px) {
	.sidebar {
		position: static;
		width: auto;
		height: auto;
		transform: none;
	}
}

.sidebar .profile {
	padding: var( --fm-space-5 );
	border-bottom: 1px solid var( --fm-portal-sidebar-hover );
}

.sidebar .welcome {
	margin: 0;
	display: flex;
	flex-direction: column;
	line-height: 1.2;
	/* Let the column shrink to its container so long names wrap instead of
	 * forcing the sidebar wider (which is what caused the horizontal scroll). */
	min-width: 0;
}

.sidebar .welcome__label {
	font-family: var( --fm-font-heading );
	text-transform: uppercase;
	font-size: 0.95rem;
	letter-spacing: 0.05em;
	color: var( --fm-portal-sidebar-muted );
}

.sidebar .welcome__name {
	font-family: var( --fm-font-heading );
	font-size: clamp( 1.7rem, 5vw, 2.1rem );
	letter-spacing: 0.005em;
	text-transform: uppercase;
	min-width: 0;
	/* First + last name share one line when they fit; the last name wraps to its
	 * own line only when the pair is too wide. */
	display: flex;
	flex-wrap: wrap;
	column-gap: 0.3em;
}

/* Each name part truncates with an ellipsis if it alone is too long for the
 * column, rather than breaking mid-word or forcing the sidebar wider. */
.sidebar .welcome__name > span {
	min-width: 0;
	max-width: 100%;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.sidebar .profile__email {
	margin: var( --fm-space-3 ) 0 0;
	font-family: var( --fm-font-accent );
	font-size: .85rem;
	word-break: break-word;
}

.sidebar .profile__email a {
	color: var( --fm-color-accent );
	font-weight: 700;
	text-decoration: none;
}

.sidebar .profile__email a:hover {
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* Nav grows to fill, so the footer (logout) sits at the very bottom. */
.sidebar__nav {
	flex: 1 1 auto;
	padding: var( --fm-space-2 ) var( --fm-space-3 );
}

.sidebar__nav ul {
	list-style: none;
	margin: 0;
	padding: 0;
	/* Rows are full-width filled boxes on hover, so they need a little air
	   between them or the highlights read as one continuous block. */
	display: flex;
	flex-direction: column;
	gap: 3px;
}

/* Per-role heading shown above each menu in the admin "all menus" sidebar view. */
#content .sidebar__nav-label {
	margin: var( --fm-space-5 ) 10px var( --fm-space-2 ) !important;
	font-family: var( --fm-font-accent );
	font-size: 0.82rem;
	letter-spacing: 0.08em;
	color: var( --fm-color-accent );
}

#content .sidebar__nav-label:first-child {
	margin-top: 0;
}

/* #content-scoped + !important so the theme's #content a (id-scoped, often an
   !important underline/colour) cannot win over these.
 *
 * The row is FULL WIDTH with a FIXED line box, not shrink-wrapped to the label.
 * That is deliberate: the hovered/current state paints a filled box, and if the
 * box were only as wide as the word, any change of size on hover would move its
 * edge out from under the cursor, un-hover it, and flicker. Fixed dimensions mean
 * only the colours change. */
#content .sidebar__nav a {
	display: block;
	width: 100%;
	padding: var( --fm-space-2 ) 10px;
	border-radius: var( --fm-radius ) !important;
	background-color: transparent;
	color: var( --fm-portal-sidebar-fg ) !important;
	text-decoration: none !important;
	text-transform: uppercase;
	font-family: var( --fm-font-heading );
	font-size: 1.45rem;
	line-height: 1.6rem;
	letter-spacing: 0.005em;
	transition: background-color 0.16s ease, color 0.16s ease;
}

#content .sidebar__nav a:hover,
#content .sidebar__nav a:focus-visible,
#content .sidebar__nav .current-menu-item > a,
#content .sidebar__nav .current_page_item > a {
	background-color: var( --fm-portal-sidebar-active-bg );
	color: var( --fm-portal-sidebar-active-fg ) !important;
	text-decoration: none !important;
}

/* Font Awesome icons admins place inside the menu label (e.g. <i class="fa-..."></i>
   VIP Home). Fixed-width column so labels align; colour follows the link, and
   swaps to the active foreground on the current item. */
#content .sidebar__nav a i {
	display: inline-block;
	width: 20px;
	height: 20px;
	margin-right: var( --fm-space-3 );
	text-align: center;
	vertical-align: middle;
	color: var( --fm-portal-sidebar-fg ) !important;
}

#content .sidebar__nav .current-menu-item > a i,
#content .sidebar__nav .current_page_item > a i {
	color: var( --fm-portal-sidebar-active-fg ) !important;
}

/* ---- The portal button ------------------------------------------------------- *
 * A plain filled rectangle: accent background, --fm-color-on-accent label, in the
 * accent face and NOT uppercased. (This replaces the animated "band" underline the
 * portal used to share with the sidebar nav; the two are now visually distinct
 * things, which is why the shared gradient rules are gone.)
 *
 * !important throughout because Elementor and Gravity Forms both ship their own
 * id/class-weighted button skins; these are the same selectors printed later but
 * they still lose to Elementor's inline-ish specificity without it.
 */
#content .button,
#content .button--secondary,
#content .ff-nominations__action,
#content .wp-block-button__link,
#content .gform_button button,
#content .redeem-form__submit,
#content .fm-rsvp__button,
#content .ff-nominations__submit,
#content .ff-button.ff-exhibitor-button,
#content .fm-rsvp__organiser {
	display: inline-flex !important;
	align-items: center;
	justify-content: center;
	width: auto !important;
	min-height: var( --fm-control-height );
	margin-left: 0 !important;
	margin-right: auto !important;
	padding: 0 var( --fm-space-3 ) !important;
	border: 0;
	border-radius: var( --fm-radius ) !important;
	background-color: var( --fm-color-accent ) !important;
	background-image: none !important;
	color: var( --fm-color-on-accent ) !important;
	font-family: var( --fm-font-accent ) !important;
	font-size: var( --fm-text-btn ) !important;
	line-height: 1 !important;
	letter-spacing: 0.005em;
	text-align: center;
	text-transform: none !important;
	text-decoration: none !important;
	box-shadow: none !important;
	cursor: pointer;
	transition: background-color 0.16s ease, color 0.16s ease;
}

/* Hover inverts: the fill becomes the hover colour and the label the accent. */
#content .button:hover,
#content .button:focus-visible,
#content .wp-block-button__link:hover,
#content .wp-block-button__link:focus-visible,
#content .gform_button button:hover,
#content .gform_button button:focus-visible,
#content .redeem-form__submit:hover,
#content .redeem-form__submit:focus-visible,
#content .fm-rsvp__button:hover,
#content .fm-rsvp__button:focus-visible,
#content .ff-nominations__submit:hover,
#content .ff-nominations__submit:focus-visible,
#content .ff-button.ff-exhibitor-button:hover,
#content .ff-button.ff-exhibitor-button:focus-visible,
#content .fm-rsvp__organiser:hover,
#content .fm-rsvp__organiser:focus-visible {
	background-color: var( --fm-color-accent-hover ) !important;
	color: var( --fm-color-accent ) !important;
}

/* ---- The secondary (ghost) button --------------------------------------------- *
 * Same shape, face and casing as the primary above — it inherits all of that from
 * the shared group — but a hairline instead of a fill, so a screen can carry two
 * actions without two competing lilac rectangles. Inverts to a solid fill on
 * hover, which is also the primary's hover, so the two feel like one family.
 *
 * Gutenberg's built-in "Outline" button style maps here, so an author choosing it
 * gets the real secondary rather than the block editor's own outline.
 */
#content .button--secondary,
#content .ff-nominations__action,
#content .wp-block-button.is-style-outline .wp-block-button__link {
	background-color: transparent !important;
	color: var( --fm-color-ink ) !important;
	box-shadow: inset 0 0 0 1px var( --fm-color-ink ) !important;
}

#content .button--secondary:hover,
#content .button--secondary:focus-visible,
#content .ff-nominations__action:hover,
#content .ff-nominations__action:focus-visible,
#content .wp-block-button.is-style-outline .wp-block-button__link:hover,
#content .wp-block-button.is-style-outline .wp-block-button__link:focus-visible {
	background-color: var( --fm-color-ink ) !important;
	color: var( --fm-color-accent ) !important;
}

/* ---- Gutenberg-authored buttons ----------------------------------------------- *
 * An editor dropping a Buttons block into page content gets the ghost, not the
 * fill: page copy can carry several of these, and a row of solid lilac rectangles
 * puts the loudest colour on the page on things nobody has reached for yet. The
 * hairline says "pressable"; the lilac is the reward for hovering.
 *
 * DELIBERATELY only this block. The portal's own controls -- the RSVP actions, the
 * nomination submit, the redemption and login submits, the exhibitor links -- keep
 * their fill, because each of those is the one thing to do on its screen and
 * should read that way. Placed after the shared groups so it wins on source order
 * at equal specificity; the geometry, face and casing still come from above.
 *
 * The "Outline" block style is left exactly as it was: it is an explicit author
 * choice and already had its own quieter treatment.
 */
#content .wp-block-button:not(.is-style-outline) .wp-block-button__link {
	background-color: transparent !important;
	color: var( --fm-color-ink ) !important;
	box-shadow: inset 0 0 0 1px var( --fm-color-ink ) !important;
	transition: background-color 0.16s ease, color 0.16s ease,
		box-shadow 0.16s ease;
}

#content .wp-block-button:not(.is-style-outline) .wp-block-button__link:hover,
#content .wp-block-button:not(.is-style-outline) .wp-block-button__link:focus-visible {
	background-color: var( --fm-color-accent ) !important;
	color: var( --fm-color-on-accent ) !important;
	box-shadow: inset 0 0 0 1px var( --fm-color-accent ) !important;
}

/* The button rules above force display:inline-block !important on RSVP controls,
   which would defeat the `hidden` attribute (a user-agent display:none) — e.g. the
   "RSVP with organiser" link must stay hidden on internal events. Re-assert hidden
   at #content specificity so it beats the button treatment. */
#content .fm-rsvp [hidden] {
	display: none !important;
}

.sidebar__footer {
	padding: var( --fm-space-4 ) var( --fm-space-5 );
	border-top: 1px solid var( --fm-portal-sidebar-hover );
}

.sidebar .logout {
	display: block;
	width: fit-content;
	padding: var( --fm-space-1 ) 10px;
	margin-left: -10px;
	color: var( --fm-portal-sidebar-fg );
	text-decoration: none;
	font-family: var( --fm-font-heading );
	font-size: 1.1rem;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	transition: background-color 0.16s ease, color 0.16s ease;
}

.sidebar .logout:hover,
.sidebar .logout:focus-visible {
	background-color: var( --fm-portal-sidebar-active-bg );
	color: var( --fm-portal-sidebar-active-fg );
	text-decoration: none;
}

/* Close button (top-right of the drawer); hidden once the sidebar is static.
   !important on colour/background: the theme's #content button skin would
   otherwise win over this class. */
.sidebar-close {
	position: absolute;
	top: 12px;
	right: 16px;
	background: transparent !important;
	border: 0;
	cursor: pointer;
	font-size: 1.75rem;
	line-height: 1;
	color: var( --fm-portal-sidebar-fg ) !important;
}

@media only screen and (min-width: 768px) {
	.sidebar-close {
		display: none;
	}
}

/* ---- Page header (hero) ------------------------------------------------------ */

.page-header {
	position: relative;
	overflow: hidden;
	display: flex;
	flex-direction: column;
	/* Bottom-left, not centred: the title anchors the corner of the image
	 * instead of floating in the middle of it. */
	justify-content: flex-end;
	align-items: flex-start;
	min-height: 34vh;
	max-height: 360px;
	padding: var( --fm-space-5 );
	background-color: var( --fm-portal-hero-bg );
	background-size: cover;
	background-position: center;
}

/* Only meaningful over a featured image; a flat brand panel needs no scrim, and
   the inline background-image on .page-header is what tells us which we have. */
.page-header[style] .overlay {
	position: absolute;
	inset: 0;
	background: linear-gradient( to top, rgba( 0, 0, 0, 0.7 ) 0%, rgba( 0, 0, 0, 0 ) 70% );
}

.entry-title {
	position: relative;
	z-index: 1;
	max-width: 1140px;
	padding: 0 !important;
	color: var( --fm-portal-hero-fg );
	width: fit-content !important;
	margin-bottom: var( --fm-space-4 );
}

/* Over a featured image the title sits on the scrim, so it takes the light
   colour regardless of what the brand panel wanted. */
.page-header[style] .entry-title {
	color: #ffffff;
}

.login-page__form .entry-title {
	color: var( --fm-portal-heading );
}

.page-header .entry-title {
	margin: 0 auto 0 0;
	max-width: 18ch;
}

/* Brand font (Adobe Fonts "din-condensed") on page titles + sidebar text only.
   Requires the Typekit kit to be loaded (see PortalProvider enqueue); falls back to
   sans-serif until then. !important on the title to beat the theme's heading font. */
#content .page-header .entry-title {
	font-family: var( --fm-font-heading ) !important;
	font-size: clamp( 2.6rem, 7vw, 5rem ) !important;
	line-height: 0.9;
	letter-spacing: 0.005em;
	text-transform: uppercase !important;
}

/* The sidebar is chrome: the heading face is its default, and the two accent
   pieces (group headings, email address) opt out individually above. */
#content .sidebar {
	font-family: var( --fm-font-heading );
}

@media only screen and (min-width: 768px) {
	.page-header {
		padding: var( --fm-space-7 );
	}
}

/* Mobile menu bar: a black strip overlaying the top of the hero that opens the
   drawer (it is a .sidebar-open trigger). Hidden once the sidebar becomes a static
   column at 768px+. */
/* !important throughout: the theme styles #content button (id-scoped), which would
   otherwise win over this class and re-skin / re-show the bar. */
.portal-mobilebar {
	display: flex !important;
	align-items: center;
	gap: var( --fm-space-2 );
	position: absolute !important;
	top: 0;
	left: 0;
	right: 0;
	z-index: 2;
	margin: 0 !important;
	padding: var( --fm-space-3 ) var( --fm-space-4 ) !important;
	width: 100% !important;
	background: var( --fm-portal-sidebar-bg ) !important;
	color: var( --fm-portal-sidebar-fg ) !important;
	border: 0 !important;
	border-radius: var( --fm-radius ) !important;
	box-shadow: none !important;
	cursor: pointer;
	font: inherit;
	font-family: var( --fm-font-heading ) !important;
	font-size: 1.2rem !important;
	text-align: left;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.portal-mobilebar:hover {
	background: var( --fm-portal-sidebar-active-bg ) !important;
	color: var( --fm-portal-sidebar-active-fg ) !important;
}

.portal-mobilebar__icon {
	flex: 0 0 auto;
}

/* Hidden on tablet + desktop (sidebar is a static column there). */
@media only screen and (min-width: 768px) {
	.portal-mobilebar {
		display: none !important;
	}
}

/* ---- Portal content --------------------------------------------------------- */

.portal-content {
	padding: var( --fm-space-5 );
}

/* ---- Editor content ----------------------------------------------------------- *
 * Page bodies are authored in Gutenberg, so its blocks have to land in the brand
 * rather than in the block editor's defaults. Headings take the heading face and
 * the same uppercase/tight treatment as the page title; buttons join the portal
 * button group above (see .wp-block-button__link there). Scoped to .portal-content
 * so the sidebar, hero and login chrome are untouched.
 */
#content .portal-content h1,
#content .portal-content h2,
#content .portal-content h3,
#content .portal-content h4,
#content .portal-content h5,
#content .portal-content h6,
#content .portal-content .wp-block-heading {
	font-family: var( --fm-font-heading );
	line-height: 1.05;
	letter-spacing: 0.005em;
	text-transform: uppercase;
	color: var( --fm-portal-heading );
}

/* Gutenberg's own alignment/width wrappers would otherwise fight the button
   rule's inline-block + auto width. */
#content .portal-content .wp-block-button {
	margin-left: 0;
}

#content .portal-content .wp-block-buttons {
	gap: var( --fm-space-3 );
}

@media only screen and (min-width: 768px) {
	.portal-content {
		padding: var( --fm-space-7 );
	}
}

/* ---- Login screen (50/50 at 768px+) ----------------------------------------- */

.login-page {
	display: grid;
	min-height: var( --fm-portal-min-height );
}

.login-page__form {
	padding: var( --fm-space-6 ) var( --fm-space-5 );
	margin: 0 auto;
	width: 100%;
}

.login-page__form-content {
	width: 100%;
	margin: 0 auto;
}

.login-page__error {
	margin-bottom: var( --fm-space-4 );
	padding: var( --fm-space-3 ) var( --fm-space-4 );
	font-size: var( --fm-text-sm );
	color: var( --fm-color-danger );
	background: var( --fm-color-danger-soft );
	border: 1px solid var( --fm-color-danger-border );
}

.login-page form {
	display: grid;
	gap: var( --fm-space-3 );
}

.login-page form p {
	margin: 0;
}

.login-page form label {
	display: block;
	font-family: var( --fm-font-body );
	font-weight: 700;
	font-size: .88rem;
	letter-spacing: 0;
	text-transform: none;
	margin-bottom: var( --fm-space-1 );
}

/* Full-width text controls stacked under their labels. The Remember Me
   checkbox stays inline inside its own label. */
.login-page form input[type="text"],
.login-page form input[type="email"],
.login-page form input[type="password"] {
	border-radius: var( --fm-radius );
	width: 100%;
	min-height: var( --fm-control-height );
	padding: var( --fm-space-2 ) var( --fm-space-3 );
}

.login-page__image {
	display: block;
	background-size: cover;
	background-position: center;
	/* Mobile: a square below the form. Desktop: the full-height right column. */
	aspect-ratio: 1 / 1;
}

/* Background video column. The featured image (set as a background-image on the
   wrapper) shows first and acts as the poster/fallback while the Vimeo iframe
   loads or if it is blocked. container-type:size lets the iframe size itself in
   cqw/cqh (container, not viewport) units so it covers this half-width column. */
.login-page__video {
	position: relative;
	overflow: hidden;
	container-type: size;
	background-size: cover;
	background-position: center;
	aspect-ratio: 1 / 1;
	background-color: var( --fm-color-dark );
}

.login-page__video iframe {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	border: 0;
	pointer-events: none;
	/* Hold a perfect 16:9 box that is always at least as large as the container
	   on both axes, so the 16:9 video fills it with no letterboxing and the
	   overflow is cropped. This is background-size:cover, but for an iframe. */
	width: max(100cqw, 177.78cqh);
	height: max(100cqh, 56.25cqw);
}

@media only screen and (min-width: 768px) {
	.login-page {
		grid-template-columns: 1fr 1fr;
		align-items: stretch;
	}

	.login-page__form {
		padding: var( --fm-space-7 );
		align-self: center;
	}

	.login-page__image,
	.login-page__video {
		aspect-ratio: auto;
		height: 100%;
	}
}

/* ---- Restricted notice ------------------------------------------------------- */

/* Centre the notice in its grid cell (the cell stretches to full portal height,
   so this centres on both axes), and centre the text within it. */
.portal-restricted {
	display: flex;
	align-items: center;
	justify-content: center;
}

.portal-restricted .portal-content {
	max-width: 640px;
	text-align: center;
}

/* The two page titles outside .page-header -- the login screen's and the
   "Restricted" notice's -- need the heading treatment set directly, since the
   .page-header rule above cannot reach them. */
.login-page__form .entry-title,
.portal-restricted .entry-title {
	font-family: var( --fm-font-heading ) !important;
	font-size: var( --fm-size-title ) !important;
	line-height: 1;
	letter-spacing: 0.005em;
	text-transform: uppercase !important;
}

.portal-restricted__message {
	margin: var( --fm-space-3 ) 0 var( --fm-space-5 );
}

.portal-restricted .button {
	padding: var( --fm-space-3 ) var( --fm-space-5 );
	text-decoration: none;
}

/* ---- Front-end login Turnstile (login.js) ------------------------------------ */

/* Inline verify-failed message above the login form. The busy spinner that
   login.js inserts beside the submit (.fm-login-spinner) lives in
   shared/components.css with the other module spinners. */
.fm-login-error {
	margin: 0 0 var( --fm-space-4 );
	color: var( --fm-color-error );
	font-size: 0.9rem;
}
