/*
 * GUERRILLA Hub — front-end styles.
 *
 * Every colour, the corner radius and the shadow are CSS custom properties with
 * semantic names. The values below are only defaults: whatever is configured in
 * Membership Hub -> Settings -> Appearance is injected as an inline override, so a
 * rebrand needs no CSS and no deploy.
 *
 * Type is deliberately absent. The hub sets `font-family: inherit`, so it picks up
 * the site's own typography unless a font is explicitly set in Settings.
 *
 * Layout intent: the member area is a single monthly "issue" — one panel holding the whole
 * drop, its sections divided by quiet rules rather than each floating in its own card. The
 * video is the anchor, the resources list beneath it, the guide opens on demand, and the
 * account settles into a footer. The gate/join states stay as standalone cards.
 */
.ghub {
	--ghub-primary: #33566E;
	--ghub-primary-dark: #244054;
	--ghub-accent: #4E8F7B;
	--ghub-surface-alt: #F5F6F7;
	--ghub-ink: #1F2933;
	--ghub-muted: #586772;
	--ghub-border: #E1E5E9;
	--ghub-radius: 14px;
	--ghub-shadow: 0 6px 24px rgba(31, 41, 51, 0.08);

	/* Shape and spacing scale. Like the colours, these are the defaults Settings overrides
	   where a setting exists (--ghub-btn-radius is Appearance -> Button radius). Everything
	   below reads from these rather than restating a number, so one card and the next cannot
	   drift by 2px, and a phone gets one set of tighter values rather than a dozen edits. */
	--ghub-btn-radius: 999px;                 /* pill by default; the setting changes it */
	--ghub-field-radius: 8px;                 /* text inputs */
	--ghub-pad-card: 24px;                    /* plan card, resource tile, video card */
	--ghub-pad-card-lg: 26px;                 /* the solid treatment, the intro panel */
	--ghub-pad-strip: 12px 16px;              /* banners and inset notices */
	--ghub-gap-grid: 16px;                    /* the plan grid and the resource grid */
	--ghub-gap-actions: 10px;                 /* every row of buttons */
	--ghub-gap-inline: 20px;                  /* space above an inline element inside a section */
	--ghub-tap: 44px;                         /* minimum height of anything tappable */
	--ghub-icon-inline: 22px;                 /* an icon sitting in a line of text */
	--ghub-icon-tile: 56px;                   /* the icon leading a resource card */
	--ghub-icon-sm: 20px;
	--ghub-icon-md: 32px;
	--ghub-icon-lg: 64px;

	/* Type scale. One knob — --ghub-fs-scale — is what Settings -> Appearance -> Text size
	   overrides, so raising legibility for a client is a setting rather than a deploy.

	   Deliberately rem-based, never px: a reader who has raised their browser's default font
	   size must still get larger text, and the scale multiplies that rather than replacing it.
	   The floor is 13px at scale 1 (it was 11px), and body-ish text — tile descriptions, seat
	   and plan notes — sits at a full 1rem where it used to sit at 0.85-0.95rem. */
	--ghub-fs-scale: 1;
	--ghub-fs-3xs: calc(0.75rem * var(--ghub-fs-scale));   /* 12px — badges: the quietest text */
	--ghub-fs-2xs: calc(0.8125rem * var(--ghub-fs-scale)); /* 13px — eyebrows */
	--ghub-fs-xs: calc(0.875rem * var(--ghub-fs-scale));   /* 14px — fine print */
	--ghub-fs-sm: calc(0.9375rem * var(--ghub-fs-scale));  /* 15px — secondary text */
	--ghub-fs-md: calc(1rem * var(--ghub-fs-scale));       /* 16px — body */
	--ghub-fs-lg: calc(1.125rem * var(--ghub-fs-scale));   /* 18px — sub-headings */
	--ghub-fs-xl: calc(1.3125rem * var(--ghub-fs-scale));  /* 21px — section headings */
	--ghub-fs-2xl: calc(1.5rem * var(--ghub-fs-scale));    /* 24px — panel headings */

	max-width: 1080px;
	margin: 0 auto;
	color: var(--ghub-ink);
	font-family: inherit;
	/* The family is inherited from the theme on purpose; the SIZE is the hub's own, so every
	   piece of text — including the ones with no rule of their own, like a tile's title or the
	   guide's paragraphs — follows the Text size setting. Without this a description with a
	   token could render larger than the title beside it. */
	font-size: var(--ghub-fs-md);
	line-height: 1.5;
}

.ghub * { box-sizing: border-box; }

/* --- Isolation from theme / page-builder styling ----------------------------------------
   The hub is dropped into arbitrary themes and Elementor, which inject margins, padding,
   list markers and box styling onto plain tags, and those leak into the hub. Neutralise them
   on the tags the hub uses so only the hub's own rules give them shape. The doubled
   .ghub.ghub prefix (specificity 0,2,0) out-ranks a builder's class-plus-tag rules
   (e.g. .elementor-widget-container p) without needing !important on every line; the hub's
   own component rules are lifted just above this where they need to win. */
.ghub.ghub :where( figure, blockquote, dl, dt, dd, fieldset ) { margin: 0; padding: 0; border: 0; }
.ghub.ghub :where( ul, ol ) { margin: 0; padding: 0; list-style: none; }
.ghub.ghub :where( li ) { margin: 0; }
.ghub.ghub :where( img, iframe ) { max-width: 100%; }
/* The hub's own text rules below are written as `.ghub .ghub-x` (specificity 0,2,0) so they
   also sit above a builder's `.widget p`-style rules and can't be repainted by them. */

.ghub a:focus-visible,
.ghub button:focus-visible,
.ghub input:focus-visible,
.ghub textarea:focus-visible,
.ghub summary:focus-visible {
	outline: 3px solid var(--ghub-accent);
	outline-offset: 2px;
}

/* Header — the monthly "drop". (Inside the issue panel the header has no outer margin; the
   panel's own section rule spaces what follows.) Uppercase eyebrows everywhere in the hub track
   at .08em, in em so the tracking scales with the text. */
.ghub .ghub-eyebrow {
	text-transform: uppercase;
	letter-spacing: .08em;
	font-size: var(--ghub-fs-2xs);
	font-weight: 700;
	color: var(--ghub-primary);
	margin-bottom: 8px;
}
.ghub-dropline {
	display: flex;
	align-items: center;
	gap: 10px;
	flex-wrap: wrap;
	margin-bottom: 8px;
}
.ghub-new {
	display: inline-block;
	background: var(--ghub-primary);
	color: #fff;
	font-size: var(--ghub-fs-2xs);
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	padding: 4px 12px;
	border-radius: 999px;
}
.ghub-dropdate {
	font-size: var(--ghub-fs-2xs);
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--ghub-muted);
}
.ghub .ghub-title {
	color: var(--ghub-primary);
	margin: 0 0 4px;
	line-height: 1.12;
	/* Fluid, and scaled by the Text size setting like everything else: the multiplier is applied
	   to the two rem bounds, leaving the viewport term to keep doing the fluid part. */
	font-size: clamp(calc(1.6rem * var(--ghub-fs-scale)), 4vw, calc(2.1rem * var(--ghub-fs-scale)));
}
.ghub .ghub-theme {
	font-size: var(--ghub-fs-lg);
	color: var(--ghub-ink);
	margin: 0;
	font-weight: 600;
}
.ghub-manage { margin: 0; }

/* Cards — used by the gate/join states, which stand alone on the page. */
.ghub-card {
	/* The same column as the issue panel, so the join card a member pays on and the panel they
	   get are the same width rather than the card sprawling to the hub's full 1080px. */
	max-width: 820px;
	background: #fff;
	border: 1px solid var(--ghub-border);
	border-radius: var(--ghub-radius);
	box-shadow: var(--ghub-shadow);
	padding: 24px 26px;
	margin: 0 auto 20px;
}
.ghub .ghub-h3 { color: var(--ghub-primary); margin: 0 0 14px; font-size: var(--ghub-fs-lg); }

/* The member area is one "issue": a single panel holding the whole monthly drop, its
   sections divided by hairlines rather than each floating in its own box. */
.ghub-issue {
	max-width: 820px;
	margin: 0 auto;
	background: #fff;
	border: 1px solid var(--ghub-border);
	/* A step up from the card radius, so the panel reads as the outer shell of the cards in it
	   and still follows the Corner radius setting. */
	border-radius: calc(var(--ghub-radius) + 6px);
	box-shadow: var(--ghub-shadow);
	padding: 32px 44px;
}
.ghub-issue .ghub-header { margin: 0; }

/* A section within the issue, divided from the one above by a hairline. The first child —
   the header, or a lone section on a component-shortcode page — never gets a rule. */
.ghub-issue > .ghub-block:not(:first-child),
.ghub-issue > .ghub-foot:not(:first-child) {
	border-top: 1px solid var(--ghub-border);
	margin-top: 30px;
	padding-top: 28px;
}

/* Section heading — one per section, kept as a wrapper (not a bare <h3>) so a component
   shortcode can render one part on its own and still look deliberate. */
.ghub-section-head { margin: 0 0 16px; }
.ghub-section-head .ghub-h3 { margin: 0; color: var(--ghub-ink); font-weight: 600; font-size: var(--ghub-fs-lg); }

/* Lead — the "what's new" note, set as an editorial intro under the title, not a boxed card. */
.ghub-lead { font-size: var(--ghub-fs-lg); line-height: 1.7; color: var(--ghub-ink); max-width: 62ch; margin: var(--ghub-gap-inline) 0 0; }
.ghub .ghub-lead p { margin: 0 0 12px; }
.ghub .ghub-lead p:last-child { margin: 0; }

/* Post-checkout confirmation — a one-time "you're in" strip at the very top of the issue. */
.ghub-banner {
	display: flex;
	align-items: center;
	gap: 13px;
	background: var(--ghub-surface-alt);
	border-radius: var(--ghub-radius);
	padding: var(--ghub-pad-strip);
	margin: 0 0 24px;
}
/* A banner that stands on its own above the issue panel (a claim strip, a removed-place
   notice) keeps the panel's width, so it does not sit 1080px wide over an 820px panel. */
.ghub > .ghub-banner { max-width: 820px; margin-left: auto; margin-right: auto; }
/* A banner with only a line of text — no heading above it — is centred on its icon; the 2px
   nudge only makes sense under a heading. */
.ghub .ghub-banner > .ghub-banner-p:only-child { margin: 0; }
.ghub-banner-icon {
	flex: none;
	width: 40px;
	height: 40px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	background: var(--ghub-accent);
	color: #fff;
}
.ghub-banner-icon svg { width: var(--ghub-icon-sm); height: var(--ghub-icon-sm); }
.ghub-banner-h { display: block; color: var(--ghub-primary); }
.ghub .ghub-banner-p { margin: 2px 0 0; color: var(--ghub-muted); }

/* Gate / join */
.ghub-gate {
	text-align: center;
	background: var(--ghub-surface-alt);
	padding: 40px 28px;
}
.ghub-gate .ghub-title { margin-bottom: 12px; }
.ghub .ghub-lede { font-size: var(--ghub-fs-lg); color: var(--ghub-muted); max-width: 34rem; margin: 0 auto 16px; }
.ghub .ghub-price {
	font-size: var(--ghub-fs-2xl);
	font-weight: 700;
	color: var(--ghub-primary);
	margin: 0 0 16px;
}
.ghub .ghub-price small { font-weight: 600; font-size: var(--ghub-fs-sm); color: var(--ghub-muted); }
.ghub .ghub-fine { font-size: var(--ghub-fs-sm); color: var(--ghub-muted); margin-top: 12px; }

/* Plan choice on the join card.
   Single plan (bulk off): a centred button with a one-line note. Two plans (bulk on): two
   cards, the bulk one featured with a "best value" badge and the solid button, so the eye
   lands on it first. */
.ghub .ghub-plans {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	align-items: stretch;
	gap: var(--ghub-gap-grid);
	margin: 12px 0 0;
	text-align: left;
}
/* One card, or an odd one left over, spans the row. Shared by the plan grid and the
   resource grid, and computed in PHP by tile_classes() for both, because a card stranded
   alone in a row is exactly what made the grid look broken the first time. */
.ghub .ghub-wide { grid-column: 1 / -1; }
@media (max-width: 600px) {
	.ghub .ghub-plans { grid-template-columns: minmax(0, 1fr); }
}
.ghub-plan { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 10px; }
/* The logged-out call to action under the plan cards: the button and the "already have an
   account?" line as one centred stack, a card's padding below the grid. The button used to be
   a bare anchor straight after the grid with no margin of its own, so it sat flush against the
   cards' bottom edge. */
.ghub .ghub-guest-actions { display: flex; flex-direction: column; align-items: center; gap: 12px; margin-top: var(--ghub-pad-card); }
.ghub .ghub-guest-actions .ghub-fine { margin: 0; }
.ghub-plan-note { font-size: var(--ghub-fs-sm); color: var(--ghub-muted); margin: 0; }

/* Same shell as a resource tile — ground, radius, padding — so the page somebody buys on
   and the page they get are visibly the same product. Emphasis on the featured card comes
   from the border and badge, never from geometry: both cards keep their place in the grid
   so they can actually be compared. */
.ghub-plan-card {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 10px;
	border: 1px solid var(--ghub-border);
	border-radius: var(--ghub-radius);
	padding: var(--ghub-pad-card);
	background: var(--ghub-surface-alt);
}
.ghub-plan-featured {
	border: 2px solid var(--ghub-primary);
}
/* Everything from the form down sits on the card's floor, so two cards with different
   amounts of text still line their buttons up — the same trick .ghub-tile-action uses. */
.ghub .ghub-plan-card form { margin-top: auto; }
.ghub .ghub-plan-seathint { font-size: var(--ghub-fs-xs); color: var(--ghub-muted); margin: 4px 0 0; min-height: 1em; }
.ghub-plan-badge {
	background: var(--ghub-accent);
	/* Computed from the accent, like every other pill — a pale accent gets dark text
	   instead of white-on-cream. Hard-coded #fff was the one pill that never learned. */
	color: var(--ghub-on-accent, #fff);
	font-size: var(--ghub-fs-2xs);
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	padding: 4px 12px;
	border-radius: 999px;
}
.ghub-plan-title { font-size: var(--ghub-fs-xl); font-weight: 700; color: var(--ghub-primary); }
.ghub-plan-card .ghub-trial-badge { margin: 0; }
/* The form is its own column with the card's rhythm: the seat picker, its hint, the school-name
   field and the button are all inside it, where the card's gap cannot reach them. */
.ghub-plan-card form { display: flex; flex-direction: column; gap: 12px; margin: 0; width: 100%; }
.ghub .ghub-plan-card .ghub-btn,
.ghub .ghub-plan-card .ghub-btn-ghost { width: 100% !important; }
.ghub .ghub-plan-name { font-weight: 600; color: var(--ghub-ink); }
.ghub .ghub-plan-seats { font-size: var(--ghub-fs-sm); color: var(--ghub-muted); margin: 0; }
.ghub .ghub-plan-seatpick {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: var(--ghub-fs-sm);
	color: var(--ghub-muted);
	margin: 0;
}

/* Plan groups — a tab each, when a hub sells to more than one kind of buyer.

   Both the tab bar and the hiding rule are gated on .is-tabbed, which only the script adds.
   Without it every group is visible and every button works: these are the controls that take
   money, so nothing may end up behind a tab that cannot be clicked. */
.ghub .ghub-offer-tabs { display: none; }
.ghub .ghub-offers.is-tabbed .ghub-offer-tabs {
	display: flex;
	gap: var(--ghub-gap-actions);
	margin: 0 0 14px;
	border-bottom: 1px solid var(--ghub-border);
}
.ghub .ghub-offers.is-tabbed .ghub-offer-group-hidden { display: none; }
.ghub .ghub-offer-tab {
	background: none !important;
	border: 0 !important;
	border-bottom: 2px solid transparent !important;
	border-radius: 0 !important;
	padding: 8px 12px !important;
	min-height: var(--ghub-tap);
	font-size: var(--ghub-fs-md) !important;
	font-weight: 600;
	color: var(--ghub-muted) !important;
	cursor: pointer;
}
.ghub .ghub-offer-tab.is-active {
	color: var(--ghub-primary) !important;
	border-bottom-color: var(--ghub-primary) !important;
}

/* Trial badge + "what's included" on the slim join card */
.ghub-trial-badge {
	display: inline-block;
	background: var(--ghub-accent);
	color: #fff;
	font-weight: 700;
	font-size: var(--ghub-fs-2xs);
	letter-spacing: .06em;
	text-transform: uppercase;
	padding: 4px 12px;
	border-radius: 999px;
	margin: 0 0 14px;
}
.ghub .ghub-includes {
	list-style: none;
	margin: 0 auto 16px;
	padding: 0;
	max-width: 20rem;
	text-align: left;
}
.ghub-includes li {
	position: relative;
	padding: 5px 0 5px 28px;
	color: var(--ghub-ink);
}
.ghub-includes li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 9px;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--ghub-accent);
	-webkit-mask: no-repeat center/10px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12l5 5L20 6'/%3E%3C/svg%3E");
	mask: no-repeat center/10px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12l5 5L20 6'/%3E%3C/svg%3E");
}

/* Buttons — fully self-defined.
   A page builder's global button styles (Elementor's especially) otherwise reshape the hub's
   buttons: repainting the colour, but also imposing their own padding, min-height, letter-
   spacing, transform and shadow. Every property that decides how the button looks is set here
   and forced, so the hub's buttons are the same wherever the hub is dropped. */
.ghub .ghub-btn,
.ghub .ghub-btn-ghost {
	/* Flex so the label is centred within the tap-target height, whatever the padding. */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: auto !important;
	min-height: var(--ghub-tap) !important;
	margin: 0;
	border-radius: var(--ghub-btn-radius) !important;
	font-family: inherit !important;
	font-weight: 600 !important;
	line-height: 1.2 !important;
	letter-spacing: normal !important;
	text-transform: none !important;
	text-decoration: none !important;
	text-align: center;
	box-shadow: none !important;
	cursor: pointer;
	transition: all 0.2s ease;
}
.ghub .ghub-btn {
	background: var(--ghub-primary) !important;
	color: #fff !important;
	border: none !important;
	padding: 12px 28px !important;
	font-size: var(--ghub-fs-md) !important;
}
.ghub .ghub-btn:hover,
.ghub .ghub-btn:focus { background: var(--ghub-primary-dark) !important; color: #fff !important; }
.ghub .ghub-btn-ghost {
	background: transparent !important;
	color: var(--ghub-primary) !important;
	border: 2px solid var(--ghub-primary) !important;
	/* Same height and label size as the solid button — the two sit side by side on the plan
	   cards and in every action row, so they must line up. The 2px border is taken out of the
	   padding, not added to the height. */
	padding: 10px 26px !important;
	font-size: var(--ghub-fs-md) !important;
}
.ghub .ghub-btn-ghost:hover,
.ghub .ghub-btn-ghost:focus { background: var(--ghub-primary) !important; color: #fff !important; }

/* Video — the anchor of the issue. Inset in the panel with rounded corners and a lift, so
   it reads as the centrepiece rather than a flush embed. Scoped and forced: a page builder's
   own margin resets were otherwise flattening the gap above it. */
.ghub .ghub-video {
	position: relative;
	margin-top: 32px !important;
	margin-bottom: 0;
	border-radius: var(--ghub-radius);
	overflow: hidden;
	box-shadow: 0 12px 34px rgba(31, 41, 51, 0.20);
}
.ghub-video iframe,
.ghub-video video {
	width: 100% !important;
	aspect-ratio: 16 / 9;
	height: auto !important;
	display: block;
	border: 0;
}
/* An invitation that did not work out, or a term about to end — the same banner, in the accent
   rather than the success colour, because it is information rather than an error the member
   caused. The text takes the room; any call to action settles to the right and wraps under on
   a narrow screen rather than crushing the message. */
.ghub .ghub-banner-warn { border-left: 3px solid var(--ghub-accent); flex-wrap: wrap; }
.ghub .ghub-banner-warn > div { flex: 1 1 16rem; }
.ghub .ghub-banner-warn > .ghub-btn { flex: 0 0 auto; margin-left: auto; }

/* The newcomer banner under the header: a pointer down the page to the newcomer video, not a
   gate. It shares the warn banner's layout but not its accent stripe down the left edge — that
   stripe marks information the member should weigh (an ending term, a failed invitation), and
   an invitation to watch something is not that. */
.ghub .ghub-cta { margin: var(--ghub-gap-inline) 0 0; border-left: 0; }
.ghub .ghub-cta .ghub-banner-h { font-weight: 600; }
/* Room above the newcomer block when the banner jumps to it, so it does not land flush to the
   top edge. */
.ghub .ghub-newcomer { scroll-margin-top: 24px; }

/* The claim-your-place confirmation. A real card, because taking a paid seat is a decision
   the recipient makes on purpose — the whole point of no longer claiming on a bare page load. */
.ghub .ghub-pending { text-align: center; }
.ghub .ghub-pending-spinner {
	display: inline-block;
	width: 28px;
	height: 28px;
	margin: 0 0 12px;
	border: 3px solid var(--ghub-border);
	border-top-color: var(--ghub-primary);
	border-radius: 50%;
	animation: ghub-spin 0.8s linear infinite;
}
@keyframes ghub-spin { to { transform: rotate(360deg); } }
@media ( prefers-reduced-motion: reduce ) { .ghub .ghub-pending-spinner { animation: none; } }
.ghub .ghub-claim { text-align: center; }
.ghub .ghub-claim .ghub-title { margin: 0 0 6px; }
.ghub .ghub-claim-to { color: var(--ghub-muted); margin: 0 0 16px; }
.ghub .ghub-claim-actions { display: flex; gap: var(--ghub-gap-actions); justify-content: center; flex-wrap: wrap; margin: 0; }

/* The fallback "watch" link when there is no embed — a plain button, not a shadowed frame. */
.ghub .ghub-video-fallback { margin-top: 32px !important; }

/* The primary video as one full-width card leading the resources section.

   Same chrome as a resource tile (icon, label pill, heading, description) so it cannot look
   like a different system, but full width with the player embedded rather than a tile that
   opens the modal — the video is what members come back for, so it plays where they land.
   The head is a column so the text reads top-down above the frame; the frame itself reuses
   the .ghub-video iframe rules above (100% width, 16/9). */
.ghub .ghub-video-card {
	display: block;
	margin: 0 0 16px;
	padding: var(--ghub-pad-card);
	border: 1px solid var(--ghub-border);
	border-radius: var(--ghub-radius);
	background: var(--ghub-surface-alt);
}
.ghub .ghub-video-card-head {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0;
	margin-bottom: 16px;
}
.ghub .ghub-tiles-solid .ghub-video-card .ghub-tile-head { align-items: center; }
/* Overrides the shared .ghub-video top margin: inside the card the frame sits directly under
   the text, so the 32px gap that separates a standalone embed from the section above it would
   read as a hole here. */
.ghub .ghub-video-card .ghub-video {
	margin-top: 0 !important;
	width: 100%;
}
.ghub .ghub-video-card .ghub-video-fallback { margin-top: 0 !important; }

/* The solid treatment paints the tiles; the video card follows so the section reads as one
   piece rather than a bordered card sitting on top of filled ones. */
.ghub .ghub-tiles-solid .ghub-video-card {
	border: 0;
	border-radius: calc(var(--ghub-radius) + 4px);
	padding: 30px var(--ghub-pad-card-lg);
	background: var(--ghub-accent);
	color: var(--ghub-on-accent, #fff);
}
.ghub .ghub-tiles-solid .ghub-video-card .ghub-tile-title,
.ghub .ghub-tiles-solid .ghub-video-card .ghub-tile-sub {
	color: var(--ghub-on-accent, #fff);
}
.ghub .ghub-tiles-solid .ghub-video-card .ghub-tile-icon { color: var(--ghub-on-accent, #fff); }

/* Resources — one card like every other section, the downloads listed inside it as rows.
   A list, not a grid of loose cards, so one resource and six both look deliberate. */
.ghub-resource-list { list-style: none; margin: 0; padding: 0; }
.ghub-resource {
	display: flex;
	align-items: center;
	gap: 16px;
	padding: 16px 0;
	border-top: 1px solid var(--ghub-border);
}
.ghub-resource:first-child { border-top: 0; padding-top: 0; }
.ghub-resource:last-child { padding-bottom: 0; }
/* The same icon at the same size as a grid tile's, so switching layout does not resize art. */
.ghub-dl-icon { flex: none; width: var(--ghub-icon-md); height: var(--ghub-icon-md); color: var(--ghub-primary); }
.ghub-resource-text { flex: 1; min-width: 0; }
.ghub-resource-title { display: block; font-weight: 600; color: var(--ghub-ink); }
.ghub-resource-sub { display: block; color: var(--ghub-muted); margin-top: 2px; }
.ghub-resource-btn { flex: none; }
.ghub-soon { flex: none; color: var(--ghub-muted); font-size: var(--ghub-fs-sm); font-style: italic; }

/* Resources as tiles — the alternative to the list, chosen in Settings.

   TWO columns, never three. Three strands a card at four items, and every CSS-only fix for
   that is an :nth-child quantity query that is wrong at some other count or breakpoint. With
   two columns an odd number leaves exactly one orphan, and the orphan is knowable in PHP —
   GHub_Shortcode::tile_classes() marks it --wide so it closes the row. That is why there is
   no clever selector here: the arithmetic happens where it can be tested.

   Written as `.ghub .ghub-x` (specificity 0,2,0) so a page builder's `.widget ul` rules
   cannot repaint it. The :where(ul,ol) isolation rule above sets only list-style, margin and
   padding, so it does not fight display:grid. */
.ghub .ghub-resource-grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--ghub-gap-grid);
	/* Stretched: cards in a row are the same height and their buttons line up along the
	   floor — the layout the client's own marketing page sets, which members arrive from. */
	align-items: stretch;
	list-style: none;
	margin: 0;
	padding: 0;
}
/* A single resource has no row to close, so it simply spans — same result as the list. */
.ghub .ghub-resource-grid--n1 { grid-template-columns: minmax(0, 1fr); }
.ghub .ghub-resource-tile {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0;
	padding: var(--ghub-pad-card);
	border: 1px solid var(--ghub-border);
	border-radius: var(--ghub-radius);
	background: var(--ghub-surface-alt);
}
/* The card's head: a LARGE icon leading it, the label beneath. The icon is the card's identity
   at a glance — the client's marketing page sets this shape and members arrive from it — so it
   is the one element allowed to be big; the label under it stays small and quiet. */
.ghub .ghub-tile-head { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.ghub .ghub-tile-badge {
	display: inline-block;
	padding: 3px 10px;
	border-radius: 999px;
	font-size: var(--ghub-fs-3xs);
	line-height: 1.3;
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	/* A TINT of the label's colour with the colour as text — a badge annotates; a solid fill
	   makes it look like a button and out-shout the title. --ghub-badge is set inline per
	   label from the colour map, and falls back to the brand colour. The plain background is
	   the fallback for a browser without color-mix(). */
	--ghub-badge: var(--ghub-primary);
	background: #fff;
	background: color-mix(in srgb, var(--ghub-badge) 13%, transparent);
	color: var(--ghub-badge);
}
.ghub-tile-icon { width: var(--ghub-icon-tile); height: var(--ghub-icon-tile); color: var(--ghub-primary); }
/* Title a full step (1.25x+) above the body; body a step below the base and lighter. That is
   what makes a card read title -> description -> action rather than four things the same size. */
.ghub .ghub-tile-title { display: block; margin-top: 12px; font-size: var(--ghub-fs-xl); line-height: 1.25; font-weight: 600; color: var(--ghub-ink); }
.ghub .ghub-tile-sub { display: block; margin-top: 6px; color: var(--ghub-muted); font-size: var(--ghub-fs-sm); line-height: 1.55; }
/* Dash-led description lines arrive as a real list (see description_html()). Lifted above the
   :where(ul) isolation reset, which zeroes list styling for the whole hub. */
.ghub .ghub-tile-sub ul { margin: 4px 0 0; padding-left: 1.1em; list-style: disc; }
.ghub .ghub-tile-sub li { margin: 0 0 2px; }
/* A card's rows — one per entry (v0.15.0: a card can hold two PDFs, or a PDF and a video) —
   drawn as a FILE LIST (v0.15.5): one line per row, a type glyph and the label taking the
   room, the actions on the right as quiet text links — "View" with a round download icon
   beside it for a PDF, "Watch" or "Open" otherwise. No buttons inside a list: a button per
   file made a two-file card read as a wall of chrome, and stacking the two actions
   (v0.15.2–0.15.4) never sat right. A card with ONE unlabelled entry keeps its single solid
   button on the floor, the shape the marketing page sets. */
.ghub .ghub-tile-items { list-style: none; margin: auto 0 0; padding: 12px 0 0; width: 100%; display: block; }
.ghub .ghub-tile-item { display: flex; align-items: center; gap: 10px; min-height: 36px; padding: 5px 0; }
.ghub .ghub-tile-item:first-child { padding-top: 0; }
.ghub .ghub-tile-item:last-child { padding-bottom: 0; }
.ghub .ghub-tile-item + .ghub-tile-item { border-top: 1px solid var(--ghub-border); padding-top: 6px; }
.ghub .ghub-tile-item-name { display: flex; align-items: center; gap: 8px; flex: 1 1 8em; min-width: 0; }
.ghub .ghub-tile-item-icon { flex: none; width: 18px; height: 18px; color: var(--ghub-muted); }
.ghub .ghub-tile-item-label { font-weight: 600; font-size: var(--ghub-fs-sm); line-height: 1.35; color: var(--ghub-ink); }
.ghub .ghub-tile-action { display: flex; align-items: center; flex: none; gap: 2px; margin-left: auto; }
/* The text action on a row. Self-defined and forced, like .ghub-linkbtn, so a theme's anchor
   rules cannot repaint it; a tint on hover so it is plainly pressable. 32px tall, with the
   row's own height around it, clears the 24px AA target. */
.ghub .ghub-rowlink {
	display: inline-flex;
	align-items: center;
	min-height: 32px;
	padding: 4px 10px !important;
	border-radius: var(--ghub-btn-radius);
	background: none !important;
	border: 0 !important;
	box-shadow: none !important;
	font: inherit !important;
	font-size: var(--ghub-fs-sm) !important;
	font-weight: 700 !important;
	color: var(--ghub-primary) !important;
	text-decoration: none !important;
	white-space: nowrap;
	cursor: pointer;
}
.ghub .ghub-rowlink:hover,
.ghub .ghub-rowlink:focus { background: rgba(0, 0, 0, .06) !important; background: color-mix(in srgb, var(--ghub-primary) 10%, transparent) !important; }
/* The round download icon beside View: a 32px target, a tint of the brand colour, the arrow
   in it. Labelled for a screen reader in the markup. */
.ghub .ghub-iconbtn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: none;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: rgba(0, 0, 0, .06) !important;
	background: color-mix(in srgb, var(--ghub-primary) 10%, transparent) !important;
	border: 0 !important;
	box-shadow: none !important;
	color: var(--ghub-primary) !important;
	text-decoration: none !important;
}
.ghub .ghub-iconbtn:hover,
.ghub .ghub-iconbtn:focus { background: color-mix(in srgb, var(--ghub-primary) 20%, transparent) !important; }
.ghub .ghub-iconbtn .ghub-icon { width: 16px; height: 16px; }
/* A video playing in its row: the frame takes the buttons' place, full width. No lift — inside
   a filled card the standalone embed's shadow reads as a smudge. The outer wrapper carries the
   spacing; the frame inside it only clips (spacing as padding on the frame put its rounded top
   corners above the player, so only the bottom pair read as rounded). */
.ghub .ghub-tile-embed { width: 100%; margin-top: 0 !important; }
.ghub .ghub-tile-embed .ghub-video { margin-top: 0 !important; box-shadow: none; }
/* In the list layout the rows sit where the single button did, hugging the right edge; a
   player on any row wraps under the text at full width instead. */
.ghub .ghub-resource .ghub-tile-items { width: auto; margin: 0; padding: 0; flex: none; justify-items: end; }
.ghub .ghub-resource--embed { flex-wrap: wrap; }
.ghub .ghub-resource--embed .ghub-tile-items { flex: 1 1 100%; width: 100%; justify-items: stretch; margin-top: 4px; }
/* The classic card's single button, compact for a card: 34px with the card's padding around
   it clears the AA target; the row-link and icon actions of a file list are sized above. */
.ghub .ghub-tile-action .ghub-btn,
.ghub .ghub-tile-action .ghub-btn-ghost {
	min-height: 34px !important;
	padding: 6px 16px !important;
	font-size: var(--ghub-fs-xs) !important;
}

/* The solid treatment — the same tile, painted as a filled block instead of a bordered card.

   Only paint and alignment change: same grid, same markup, same orphan rule, so switching
   between the two is a radio button rather than a second layout to keep working. The text
   colour is --ghub-on-accent, computed in PHP from the configured accent, so a client who
   picks a pale accent gets dark text rather than white-on-cream. */
.ghub .ghub-tiles-solid .ghub-resource-tile {
	align-items: center;
	text-align: center;
	gap: 0;
	padding: 30px var(--ghub-pad-card-lg);
	border: 0;
	border-radius: calc(var(--ghub-radius) + 4px);
	background: var(--ghub-accent);
	color: var(--ghub-on-accent, #fff);
}
.ghub .ghub-tiles-solid .ghub-tile-head { align-items: center; }
.ghub .ghub-tiles-solid .ghub-tile-badge {
	background: rgba(255, 255, 255, .2);
	color: var(--ghub-on-accent, #fff);
}
.ghub .ghub-tiles-solid .ghub-tile-title,
.ghub .ghub-tiles-solid .ghub-tile-sub {
	color: var(--ghub-on-accent, #fff);
}
.ghub .ghub-tiles-solid .ghub-tile-sub { line-height: 1.6; }
/* Larger here because the icon is doing the work a border does on a plain card: it is what
   makes one tile distinguishable from the next at a glance. */
.ghub .ghub-tiles-solid .ghub-tile-icon,
.ghub .ghub-tiles-solid .ghub-tile-img {
	width: var(--ghub-icon-lg);
	height: var(--ghub-icon-lg);
	color: var(--ghub-on-accent, #fff);
}
.ghub .ghub-tiles-solid .ghub-tile-items { padding-top: 16px; }
.ghub .ghub-tiles-solid .ghub-tile-item { text-align: left; }
.ghub .ghub-tiles-solid .ghub-tile-item + .ghub-tile-item { border-top-color: rgba(255, 255, 255, .28); }
.ghub .ghub-tiles-solid .ghub-tile-item-label,
.ghub .ghub-tiles-solid .ghub-tile-item-icon,
.ghub .ghub-tiles-solid .ghub-rowlink,
.ghub .ghub-tiles-solid .ghub-iconbtn { color: var(--ghub-on-accent, #fff) !important; }
.ghub .ghub-tiles-solid .ghub-iconbtn { background: rgba(255, 255, 255, .18) !important; }
.ghub .ghub-tiles-solid .ghub-rowlink:hover,
.ghub .ghub-tiles-solid .ghub-rowlink:focus,
.ghub .ghub-tiles-solid .ghub-iconbtn:hover,
.ghub .ghub-tiles-solid .ghub-iconbtn:focus { background: rgba(255, 255, 255, .3) !important; }

/* A per-item image, for artwork no shared icon set can supply — a client's own illustration
   rather than a line glyph. Sized to match the SVG so the two are interchangeable. */
.ghub .ghub-tile-img {
	width: var(--ghub-icon-tile);
	height: var(--ghub-icon-tile);
	object-fit: contain;
	display: block;
}

/* The video dialog. A resource of type Video plays here rather than sending the member off
   to the video host — they paid for this, so it should not read like a link to somewhere else.

   Fixed to the viewport rather than absolutely positioned in the hub, because the hub sits
   inside whatever the theme puts around it and an absolutely positioned overlay would be
   clipped by any ancestor with overflow set. */
.ghub-video-modal[hidden] { display: none !important; }
.ghub-video-modal {
	position: fixed;
	inset: 0;
	z-index: 100000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 24px;
}
.ghub-video-backdrop { position: absolute; inset: 0; background: rgba(17, 17, 17, .78); }
.ghub-video-dialog {
	position: relative;
	width: min(1000px, 100%);
	background: #111;
	border-radius: var(--ghub-radius);
	overflow: hidden;
	box-shadow: 0 18px 50px rgba(0, 0, 0, .45);
}
.ghub-video-bar {
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 12px 14px 12px 18px;
	color: #fff;
}
.ghub .ghub-video-modal-title,
.ghub-video-modal-title {
	margin: 0;
	flex: 1;
	min-width: 0;
	font-size: var(--ghub-fs-md);
	font-weight: 600;
	color: #fff;
}
/* The primary close affordance on a phone: a full tap target, not a bare glyph. */
.ghub-video-close {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--ghub-tap);
	height: var(--ghub-tap);
	background: none !important;
	border: 0 !important;
	color: #fff !important;
	font-size: var(--ghub-fs-2xl);
	line-height: 1;
	padding: 0 !important;
	cursor: pointer;
	border-radius: 50%;
}
/* 16:9, which every provider we build a player URL for uses. */
.ghub-video-frame { position: relative; aspect-ratio: 16 / 9; background: #000; }
.ghub-video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Stop the page behind scrolling while the dialog is open. */
body.ghub-modal-open { overflow: hidden; }
@media (max-width: 600px) {
	.ghub-video-modal { padding: 12px; }
}

/* The wording panel above the resources — what a member gets each month.

   Painted like a tile so the section reads as one set: in the solid treatment it takes the
   same accent fill, and in the plain one the quieter panel colour, which keeps it from
   shouting over cards it is only introducing. */
.ghub .ghub-resource-intro {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 8px;
	padding: var(--ghub-pad-card-lg);
	margin: 0 0 16px;
	border-radius: calc(var(--ghub-radius) + 4px);
	background: var(--ghub-surface-alt);
}
.ghub .ghub-intro-eyebrow {
	text-transform: uppercase;
	letter-spacing: .08em;
	font-size: var(--ghub-fs-2xs);
	font-weight: 700;
}
.ghub .ghub-intro-h { font-size: var(--ghub-fs-2xl); font-weight: 700; line-height: 1.25; }
.ghub .ghub-intro-p { margin: 0; max-width: 46ch; }
.ghub .ghub-tiles-solid .ghub-resource-intro {
	background: var(--ghub-accent);
	color: var(--ghub-on-accent, #fff);
}
.ghub .ghub-tiles-solid .ghub-intro-p { color: var(--ghub-on-accent, #fff); }
@media (max-width: 600px) {
	.ghub .ghub-intro-h { font-size: var(--ghub-fs-xl); }
}

/* The wide image above the resources. Decoration, not gated content. */
.ghub .ghub-resource-banner { margin: 0 0 16px; }
.ghub .ghub-resource-banner-img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: var(--ghub-radius);
}

/* Seats — who else is on a shared membership, and how to add somebody. Only the person
   who bought the membership ever sees this block. */
/* The management panel: one line by default, the full roster behind "Manage places". A <details>,
   so it opens without JavaScript. The summary is the strip; the marker is hidden and the ghost
   button inside it is the visible affordance (aria-hidden — the <summary> itself is the control). */
.ghub .ghub-seats-panel > summary { list-style: none; }
.ghub .ghub-seats-panel > summary::-webkit-details-marker { display: none; }
.ghub .ghub-seats-summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px 16px;
	flex-wrap: wrap;
	cursor: pointer;
	padding: 14px 18px;
	border: 1px solid var(--ghub-border);
	border-radius: var(--ghub-radius);
	background: var(--ghub-surface-alt);
}
.ghub .ghub-seats-summary-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ghub .ghub-seats-org { font-weight: 600; color: var(--ghub-ink); }
.ghub .ghub-seats-facts { color: var(--ghub-muted); font-size: var(--ghub-fs-sm); }
/* The toggle is a span painted as a ghost button; the whole strip is the click target. */
.ghub .ghub-seats-toggle { flex: none; pointer-events: none; }
.ghub .ghub-seats-panel[open] > .ghub-seats-summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
/* The toggle is inline-flex, so its ::after is a flex item of its own and a space inside the
   content would be trimmed; the gap is a margin. */
.ghub .ghub-seats-toggle::after { margin-left: 8px; font-size: .85em; }
.ghub .ghub-seats-panel[open] .ghub-seats-toggle::after { content: "\25B4"; }
.ghub .ghub-seats-panel:not([open]) .ghub-seats-toggle::after { content: "\25BE"; }
.ghub .ghub-seats-body {
	padding: 4px 18px 18px;
	border: 1px solid var(--ghub-border);
	border-top: 0;
	border-radius: 0 0 var(--ghub-radius) var(--ghub-radius);
}
/* Secondary actions on a list row: real submit buttons styled as links. Fully self-defined and
   forced, for the same page-builder reason the .ghub-btn rules are. */
.ghub .ghub-linkbtn {
	display: inline-flex;
	align-items: center;
	min-height: var(--ghub-tap);
	margin: 0;
	padding: 0 2px !important;
	background: none !important;
	border: 0 !important;
	box-shadow: none !important;
	font: inherit !important;
	font-size: var(--ghub-fs-sm) !important;
	font-weight: 600 !important;
	color: var(--ghub-primary) !important;
	text-decoration: none !important;
	cursor: pointer;
}
.ghub .ghub-linkbtn:hover,
.ghub .ghub-linkbtn:focus { text-decoration: underline !important; }
.ghub .ghub-linkbtn-danger { color: var(--ghub-muted) !important; }
.ghub .ghub-linkbtn-danger:hover,
.ghub .ghub-linkbtn-danger:focus { color: #8a1a1a !important; }
/* The office-manager rule: one line, the explanation behind a disclosure. */
.ghub .ghub-seat-why { margin: 0 0 4px; font-size: var(--ghub-fs-sm); color: var(--ghub-muted); }
.ghub .ghub-seat-why > summary { cursor: pointer; padding: 6px 0; }
.ghub .ghub-seat-why > p { margin: 4px 0 8px; max-width: 60ch; }
.ghub .ghub-seat-error {
	color: var(--ghub-ink);
	background: var(--ghub-surface-alt);
	border-left: 3px solid var(--ghub-accent);
	padding: var(--ghub-pad-strip);
	margin: 0 0 12px;
}
.ghub .ghub-seat-list { list-style: none; margin: 0 0 6px; padding: 0; }
/* The buyer's own line, set apart from the people they invited — they are on the membership
   by paying for it, not by holding an invitation. */
.ghub .ghub-seat-mine { border-top: 0; }
.ghub .ghub-seat-mine .ghub-seat-who { font-style: italic; }
.ghub .ghub-seat-note { color: var(--ghub-muted); font-size: var(--ghub-fs-sm); margin: 0 0 14px; }
/* Group headings on the roster — "Members" and "Pending invitations" as two answers. */
.ghub .ghub-seat-group {
	margin: 18px 0 4px;
	font-size: var(--ghub-fs-xs);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .08em;
	color: var(--ghub-muted);
}
/* The required school-name field on a shared plan's join card. */
.ghub .ghub-plan-orgname { display: flex; flex-direction: column; gap: 4px; margin: 0; text-align: left; }

/* Every text, email and number field a member types into — one rule, so the school-name box on
   the join card, the invite address, the rename field, the copy-link field and both place-count
   boxes are the same height as the button beside them and the same shape as each other. The
   number inputs previously had no rule at all and rendered as the browser's bare spinner. */
.ghub .ghub-plan-orgname input[type="text"],
.ghub .ghub-seat-name-form input[type="text"],
.ghub .ghub-copy-field,
.ghub .ghub-seat-invite input[type="email"],
.ghub .ghub-plan-seatpick input,
.ghub .ghub-seat-add input[type="number"] {
	padding: 10px 12px;
	min-height: var(--ghub-tap);
	border: 1px solid var(--ghub-border);
	border-radius: var(--ghub-field-radius);
	background: #fff;
	color: var(--ghub-ink);
	font: inherit;
	font-size: var(--ghub-fs-md);
}
.ghub .ghub-plan-seatpick input,
.ghub .ghub-seat-add input[type="number"] { width: 6em; }
/* Naming the membership: quiet by default, a one-line form when opened. */
.ghub .ghub-seat-name { margin: 14px 0 0; font-size: var(--ghub-fs-sm); }
.ghub .ghub-seat-name > summary { cursor: pointer; color: var(--ghub-muted); padding: 4px 0; }
.ghub .ghub-seat-name-form { display: flex; gap: var(--ghub-gap-actions); margin-top: 8px; flex-wrap: wrap; }
.ghub .ghub-seat-name-form input[type="text"] { flex: 1 1 220px; }
.ghub .ghub-seat {
	display: flex;
	align-items: center;
	gap: 14px;
	flex-wrap: wrap;
	padding: 12px 0;
	border-top: 1px solid var(--ghub-border);
}
.ghub .ghub-seat:first-child { border-top: 0; }
.ghub .ghub-seat-who { flex: 1; min-width: 0; font-weight: 600; color: var(--ghub-ink); }
/* The email under the name: the secondary identifier, a step down and lighter. */
.ghub .ghub-seat-email { display: block; font-weight: 400; font-size: var(--ghub-fs-sm); color: var(--ghub-muted); word-break: break-all; }
.ghub .ghub-seat-status { color: var(--ghub-muted); font-size: var(--ghub-fs-sm); }
.ghub .ghub-seat-actions { display: flex; align-items: center; gap: 0 14px; flex-wrap: wrap; }
.ghub .ghub-seat-actions form { margin: 0; }
/* A revealed invite link drops onto its own full-width line below the seat row. */
.ghub .ghub-seat-link { flex: 1 1 100%; margin: 4px 0 0; }
.ghub .ghub-seat-link-label { display: block; color: var(--ghub-muted); font-size: var(--ghub-fs-xs); margin-bottom: 6px; }
.ghub .ghub-copy { display: flex; gap: var(--ghub-gap-actions); align-items: stretch; }
.ghub .ghub-copy-field { flex: 1 1 auto; min-width: 0; }
.ghub .ghub-copy-btn { flex: 0 0 auto; white-space: nowrap; }
.ghub .ghub-seat-invite { display: flex; gap: var(--ghub-gap-actions); flex-wrap: wrap; margin: 0; }
.ghub .ghub-seat-add {
	display: flex;
	align-items: center;
	gap: var(--ghub-gap-actions);
	flex-wrap: wrap;
	margin: 10px 0 0;
	color: var(--ghub-muted);
	font-size: var(--ghub-fs-sm);
}
.ghub .ghub-seat-add label { display: inline-flex; align-items: center; gap: 8px; }
.ghub .ghub-seat-price { color: var(--ghub-muted); font-size: var(--ghub-fs-sm); }
.ghub .ghub-seat-invite input[type="email"] { flex: 1 1 15em; min-width: 0; }

/* Archive of past releases — a block within the issue, listed plainly. */
.ghub-archive-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 0;
}
/* Each past release is a link the height of a tap target, rather than a 24px line every 32px. */
.ghub-archive-list a {
	display: inline-block;
	padding: 10px 0;
	color: var(--ghub-primary);
	text-decoration: none;
	font-weight: 600;
}
.ghub-archive-list a:hover { text-decoration: underline; }
/* "Viewing a past release" notice — a quiet inset strip near the top of the issue. */
.ghub-viewing-past {
	background: var(--ghub-surface-alt);
	border-radius: var(--ghub-radius);
	padding: var(--ghub-pad-strip);
	margin: var(--ghub-gap-inline) 0 0;
}
.ghub .ghub-viewing-past p { margin: 0; color: var(--ghub-muted); }
/* An operator's preview of a release members cannot see yet: the same strip, in the accent,
   so it cannot be mistaken for the live page. */
.ghub .ghub-previewing { border-left: 3px solid var(--ghub-accent); }
.ghub .ghub-previewing p { color: var(--ghub-ink); }

/* Account — the membership summary, settled into a footer at the end of the issue rather
   than a boxed panel. Utility, not content; will hand off to GUERRILLA-Account. */
.ghub-foot { display: flex; justify-content: space-between; align-items: center; gap: 16px 24px; flex-wrap: wrap; }
.ghub-foot-h { display: block; font-size: var(--ghub-fs-2xs); letter-spacing: .08em; text-transform: uppercase; color: var(--ghub-muted); margin: 0 0 6px; }
.ghub-foot-list { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.ghub-foot-item { display: flex; align-items: baseline; gap: 6px; }
.ghub-foot-k { color: var(--ghub-muted); font-size: var(--ghub-fs-sm); }
.ghub-foot-v { font-weight: 600; }
.ghub-foot-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--ghub-gap-actions); }

/* The newcomer video block at the foot of the content — the banner up top jumps here. A section
   like the others (heading, a line of description), with the player under the text at the
   inline gap rather than the standalone embed's 32px, so heading, text and player read as one. */
.ghub .ghub-newcomer-sub { color: var(--ghub-muted); max-width: 62ch; margin: 0; }
.ghub .ghub-newcomer-sub ul { margin: 0; padding-left: 1.2em; }
.ghub .ghub-newcomer .ghub-video,
.ghub .ghub-newcomer .ghub-video-fallback { margin-top: var(--ghub-gap-inline) !important; }

/* "Coming next" — a quiet foot line under the account footer, written on the release by its
   editor. Deliberately NOT a .ghub-block: the section divider would otherwise put a second
   hairline and 58px of space between the footer and one line of text. */
.ghub .ghub-next {
	display: block;
	border-top: 1px solid var(--ghub-border);
	padding-top: 16px;
	margin-top: var(--ghub-gap-inline);
}
.ghub .ghub-next-line { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 10px; }
.ghub .ghub-next-name { font-weight: 600; color: var(--ghub-ink); }
.ghub .ghub-next-when { color: var(--ghub-muted); }
.ghub .ghub-next-sub { flex: 1 1 100%; color: var(--ghub-muted); }

/* Welcome / holding state — the first thing a member sees before the first drop */
.ghub-welcome { text-align: center; margin-top: 12px; }
.ghub-welcome .ghub-title { font-size: var(--ghub-fs-xl); margin: 0 0 8px; }
.ghub .ghub-welcome p { color: var(--ghub-muted); margin: 0 auto; max-width: 34rem; }
.ghub-welcome .ghub-statusrow {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	background: var(--ghub-surface-alt);
	color: var(--ghub-primary);
	border-radius: 999px;
	padding: 6px 14px;
	font-size: var(--ghub-fs-sm);
	font-weight: 600;
	margin: 16px 0 0;
}

/* Guide — a calm long-read that opens on demand. Without JS the class is never added,
   so the full guide shows and the button stays hidden (progressive enhancement). */
.ghub .ghub-guide .ghub-prose { color: var(--ghub-ink); line-height: 1.7; max-width: 68ch; }
/* The guide body is post content, so its paragraphs are bare <p>. Give them their own rhythm
   (above a builder's paragraph rules) rather than relying on the theme's default spacing. */
.ghub .ghub-guide .ghub-prose p { margin: 0 0 1em; }
.ghub .ghub-guide .ghub-prose p:last-child { margin-bottom: 0; }
.ghub-guide-body { position: relative; }
/* 20em is the 320px hub.js collapses above, at the base size — the two must agree, or the
   fade covers a growing share of a box that never grew when Text size is raised. */
.ghub-guide.is-collapsible .ghub-guide-body { max-height: 20em; overflow: hidden; }
.ghub-guide.is-collapsible.is-expanded .ghub-guide-body { max-height: none; }
.ghub-guide.is-collapsible:not(.is-expanded) .ghub-guide-body::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 5.5em;
	background: linear-gradient(to bottom, rgba(255, 255, 255, 0), #fff);
	pointer-events: none;
}
.ghub-guide-more { margin-top: 16px; }

/* Narrow columns: reclaim the horizontal room the card padding eats, so a member on a
   phone (or a theme with a slim content column) reads comfortably rather than in a squeeze. */
@media (max-width: 600px) {
	/* Written as `.ghub .ghub-x` like the base rules, or they lose on specificity and never
	   apply — which is exactly what had happened to the title size and the includes list. */
	.ghub-issue { padding: 22px 18px; border-radius: calc(var(--ghub-radius) + 2px); }
	.ghub-card { padding: 18px 16px; }
	.ghub-gate { padding: 32px 18px; }
	.ghub .ghub-title { font-size: var(--ghub-fs-2xl); }
	.ghub .ghub-includes { max-width: none; }
	/* Cards inside an 18px-padded panel on a 375px screen: tighter, or the text column is
	   squeezed to ~285px. */
	.ghub .ghub-plan-card,
	.ghub .ghub-resource-tile,
	.ghub .ghub-video-card { padding: 18px; }
	.ghub .ghub-tiles-solid .ghub-resource-tile,
	.ghub .ghub-tiles-solid .ghub-video-card { padding: 22px 18px; }
	.ghub .ghub-resource-intro { padding: 20px 16px; }
	.ghub-issue > .ghub-block:not(:first-child),
	.ghub-issue > .ghub-foot:not(:first-child) { margin-top: 22px; padding-top: 20px; }
	/* One column below the breakpoint, where two would be two narrow columns of nothing.
	   Every tile spans, so the orphan rule becomes a no-op rather than something to undo. */
	.ghub .ghub-resource-grid { grid-template-columns: minmax(0, 1fr); }
	/* Actions go full width on a phone — in the grid, on the roster and in the footer alike,
	   not only the list layout's download button. */
	.ghub .ghub-seat-invite .ghub-btn,
	.ghub .ghub-seat-add .ghub-btn-ghost,
	.ghub .ghub-foot-actions .ghub-btn,
	.ghub .ghub-foot-actions .ghub-btn-ghost { width: 100% !important; }
	.ghub .ghub-seat-actions { flex: 1 1 100%; }
	.ghub .ghub-seats-summary { padding: 12px 14px; }
	.ghub .ghub-seats-body { padding: 4px 14px 14px; }
	.ghub .ghub-foot-actions form { width: 100%; }
	.ghub-foot { flex-direction: column; align-items: stretch; }
	.ghub-foot-actions { flex-direction: column; align-items: stretch; }
	.ghub-video-bar { padding: 10px 12px; }
}
@media (max-width: 440px) {
	/* Let the actions drop below the title rather than crushing it. */
	.ghub-resource { flex-wrap: wrap; }
	.ghub .ghub-resource .ghub-tile-items { flex: 1 1 100%; width: 100%; justify-items: stretch; }
	.ghub-resource-btn { width: 100%; text-align: center; }
}
@media (prefers-reduced-motion: reduce) {
	.ghub-btn, .ghub-btn-ghost { transition: none; }
}
