/* Grade Builder - front-end styles */

:root {
	--grade-primary: #2563eb;
	--grade-secondary: #7c3aed;
	--grade-text: #1f2933;
	--grade-heading: #0b1220;
	--grade-muted: #6b7280;
	--grade-surface: #ffffff;
	--grade-border: #e5e7eb;
	--grade-radius: 8px;
	--grade-content-width: 1200px;
	--grade-anim-duration: 600ms;
}

.grade-content { width: 100%; }
.grade-el { position: relative; }
.grade-el > * { max-width: 100%; }

/* ------------------------------------------------------------------ */
/* Vertical rhythm                                                    */
/* ------------------------------------------------------------------ */
/*
 * Elements had no spacing between them at all - every one sat flush against
 * the next, and whatever separation a page appeared to have came from the
 * elements' own internal padding, which differs per element. A page therefore
 * looked wrong the moment it was built, before anyone touched a margin control.
 *
 * The rule follows how reading actually works: a heading belongs to what comes
 * after it, so the gap *below* a heading is small and the gap *above* it is
 * large. That is what separates one block of content from the next without
 * anybody setting a margin.
 *
 * All three are custom properties, so a site can retune the whole page from
 * Grade > Options rather than by editing every element.
 */
:root {
	--grade-element-gap: 28px;
	--grade-heading-gap: 14px;
	--grade-block-gap: 56px;
}

/*
 * Only inside a column. A column stacks its children vertically, so a top
 * margin is the gap between them. A container's own children are laid out as a
 * horizontal flex row - a header's logo beside its menu - where a top margin
 * would simply push one of them out of line. Containers get their spacing from
 * the gap property instead.
 */
/*
 * Wrapped in :where() so every one of these is a default rather than a rule.
 *
 * A per-element margin is emitted as a single class - .grade-el-sp43 - which is
 * one point of specificity. Every selector below carried two or more, so the
 * rhythm quietly beat the number the editor had typed into the Margin field,
 * and only for elements that happened to follow something else in a column. It
 * looked like the setting was ignored at random: the same value worked on the
 * first element in a column and did nothing on the second. :where() contributes
 * no specificity at all, so the spacing still applies to everything nobody has
 * an opinion about, and loses the moment anybody does.
 */
:where(.grade-column__inner > .grade-el + .grade-el) {
	margin-top: var(--grade-element-gap);
}

/* A heading introduces what follows it: keep them close. */
:where(.grade-column__inner > .grade-el--heading + .grade-el) {
	margin-top: var(--grade-heading-gap);
}

/* ...and give it room above, to break from the previous block. */
:where(.grade-column__inner > .grade-el + .grade-el--heading) {
	margin-top: var(--grade-block-gap);
}

/* An eyebrow is a label on the heading directly beneath it. */
:where(.grade-column__inner > .grade-el--eyebrow + .grade-el--heading) {
	margin-top: 8px;
}

/*
 * A spacer or separator *is* the gap - it was added precisely because somebody
 * wanted to control that distance by hand. Adding rhythm on top of it would
 * silently make every hand-set gap bigger than the number the person typed.
 */
:where(.grade-column__inner > .grade-el + .grade-el--spacer),
:where(.grade-column__inner > .grade-el + .grade-el--separator),
:where(.grade-column__inner > .grade-el--spacer + .grade-el),
:where(.grade-column__inner > .grade-el--separator + .grade-el) {
	margin-top: 0;
}

/* ---------- Container ---------- */
.grade-container { width: 100%; position: relative; background-repeat: no-repeat; }
.grade-container > .grade-container__inner {
	--grade-gap: 24px;
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	gap: var(--grade-gap);
	margin: 0 auto;
	width: 100%;
	position: relative;
	z-index: 1;
}
.grade-container--boxed > .grade-container__inner,
.grade-container--fullwidth > .grade-container__inner {
	max-width: var(--grade-content-width);
	padding-left: 20px;
	padding-right: 20px;
}
.grade-container--full > .grade-container__inner { max-width: none; }
.grade-container--equal > .grade-container__inner > .grade-column { align-self: stretch; }

/* ---------- Background video ----------
   Sits behind the content of any container. The container is already
   position:relative and its inner is z-index:1 (see above), so this layer needs
   no z-index of its own to end up underneath - and not giving it one keeps it
   out of the way of anything else stacking inside the section.

   object-fit:cover is what makes it behave like background-size:cover. Without
   it the video keeps its own aspect ratio and letterboxes inside the section,
   which looks like a bug and is the single most common way this is got wrong. */
.grade-bgvideo { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.grade-bgvideo video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Background Treatment.
   --------------------------------------------------------------------------
   Applied to the background layer, never to the wrapper. `filter` on the
   wrapper cascades to every descendant, so dimming a photograph also dims the
   headline standing on it - and it makes the element a containing block, which
   breaks fixed-position children. Both were true of the sliders this replaced.

   The variable is unset on most containers, and `filter: ` with nothing after
   it is invalid and ignored, so this costs nothing where it is not used. */
.grade-el--container { --grade-bg-filter: ; }
.grade-el--container .grade-bgvideo video { filter: var(--grade-bg-filter); }

/* A background *image* lives on the element itself, so it cannot be filtered
   without filtering the content too. It gets its own painted layer instead. */
.grade-el--container[style*="--grade-bg-filter"]::before {
	content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
	background-image: inherit; background-size: inherit;
	background-position: inherit; background-repeat: inherit;
	filter: var(--grade-bg-filter);
}
.grade-bgvideo__scrim { position: absolute; inset: 0; }

/* Somebody who has asked their system to stop animating things has asked for
   this too. The poster frame is the section's own background image, so hiding
   the video leaves the picture rather than a hole. */
@media (prefers-reduced-motion: reduce) {
	.grade-bgvideo video { display: none; }
}

/* ---------- Columns ---------- */
.grade-column {
	flex: 1 1 0;
	min-width: 0;
	display: flex;
	position: relative;
}
.grade-column__inner {
	display: flex;
	flex-direction: column;
	width: 100%;
	min-width: 0;
}

.grade-col-1_1 { flex: 0 0 100%; max-width: 100%; }
.grade-col-1_2 { flex: 0 0 calc(50% - (var(--grade-gap, 24px) / 2)); max-width: calc(50% - (var(--grade-gap, 24px) / 2)); }
.grade-col-1_3 { flex: 0 0 calc(33.3333% - (var(--grade-gap, 24px) * 2 / 3)); max-width: calc(33.3333% - (var(--grade-gap, 24px) * 2 / 3)); }
.grade-col-2_3 { flex: 0 0 calc(66.6666% - (var(--grade-gap, 24px) / 3)); max-width: calc(66.6666% - (var(--grade-gap, 24px) / 3)); }
.grade-col-1_4 { flex: 0 0 calc(25% - (var(--grade-gap, 24px) * 3 / 4)); max-width: calc(25% - (var(--grade-gap, 24px) * 3 / 4)); }
.grade-col-3_4 { flex: 0 0 calc(75% - (var(--grade-gap, 24px) / 4)); max-width: calc(75% - (var(--grade-gap, 24px) / 4)); }
.grade-col-1_5 { flex: 0 0 calc(20% - (var(--grade-gap, 24px) * 4 / 5)); max-width: calc(20% - (var(--grade-gap, 24px) * 4 / 5)); }
.grade-col-2_5 { flex: 0 0 calc(40% - (var(--grade-gap, 24px) * 3 / 5)); max-width: calc(40% - (var(--grade-gap, 24px) * 3 / 5)); }
.grade-col-3_5 { flex: 0 0 calc(60% - (var(--grade-gap, 24px) * 2 / 5)); max-width: calc(60% - (var(--grade-gap, 24px) * 2 / 5)); }
.grade-col-4_5 { flex: 0 0 calc(80% - (var(--grade-gap, 24px) / 5)); max-width: calc(80% - (var(--grade-gap, 24px) / 5)); }
.grade-col-1_6 { flex: 0 0 calc(16.6666% - (var(--grade-gap, 24px) * 5 / 6)); max-width: calc(16.6666% - (var(--grade-gap, 24px) * 5 / 6)); }
.grade-col-5_6 { flex: 0 0 calc(83.3333% - (var(--grade-gap, 24px) / 6)); max-width: calc(83.3333% - (var(--grade-gap, 24px) / 6)); }

@media (max-width: 1024px) {
	.grade-col-tablet-1_1 { flex: 0 0 100%; max-width: 100%; }
	.grade-col-tablet-1_2 { flex: 0 0 calc(50% - (var(--grade-gap, 24px) / 2)); max-width: calc(50% - (var(--grade-gap, 24px) / 2)); }
	.grade-col-tablet-1_3 { flex: 0 0 calc(33.3333% - (var(--grade-gap, 24px) * 2 / 3)); max-width: calc(33.3333% - (var(--grade-gap, 24px) * 2 / 3)); }
	.grade-col-tablet-2_3 { flex: 0 0 calc(66.6666% - (var(--grade-gap, 24px) / 3)); max-width: calc(66.6666% - (var(--grade-gap, 24px) / 3)); }
	.grade-col-tablet-1_4 { flex: 0 0 calc(25% - (var(--grade-gap, 24px) * 3 / 4)); max-width: calc(25% - (var(--grade-gap, 24px) * 3 / 4)); }
	.grade-col-tablet-3_4 { flex: 0 0 calc(75% - (var(--grade-gap, 24px) / 4)); max-width: calc(75% - (var(--grade-gap, 24px) / 4)); }
	.grade-col-tablet-1_5 { flex: 0 0 calc(20% - (var(--grade-gap, 24px) * 4 / 5)); max-width: calc(20% - (var(--grade-gap, 24px) * 4 / 5)); }
	.grade-col-tablet-1_6 { flex: 0 0 calc(16.6666% - (var(--grade-gap, 24px) * 5 / 6)); max-width: calc(16.6666% - (var(--grade-gap, 24px) * 5 / 6)); }
}

@media (max-width: 767px) {
	/* Columns stack by default on mobile unless explicitly overridden. */
	.grade-container > .grade-container__inner > .grade-column { flex: 0 0 100%; max-width: 100%; }
	.grade-col-mobile-1_1 { flex: 0 0 100% !important; max-width: 100% !important; }
	.grade-col-mobile-1_2 { flex: 0 0 calc(50% - (var(--grade-gap, 24px) / 2)) !important; max-width: calc(50% - (var(--grade-gap, 24px) / 2)) !important; }
	.grade-col-mobile-1_3 { flex: 0 0 calc(33.3333% - (var(--grade-gap, 24px) * 2 / 3)) !important; max-width: calc(33.3333% - (var(--grade-gap, 24px) * 2 / 3)) !important; }
	.grade-col-mobile-2_3 { flex: 0 0 calc(66.6666% - (var(--grade-gap, 24px) / 3)) !important; max-width: calc(66.6666% - (var(--grade-gap, 24px) / 3)) !important; }
	.grade-col-mobile-1_4 { flex: 0 0 calc(25% - (var(--grade-gap, 24px) * 3 / 4)) !important; max-width: calc(25% - (var(--grade-gap, 24px) * 3 / 4)) !important; }
}

/* ---------- Heading & text ---------- */
.grade-heading__text { margin: 0; color: var(--grade-heading); }
.grade-heading__link { text-decoration: none; color: inherit; }
.grade-text__inner { color: var(--grade-text); }
.grade-text__inner > *:first-child { margin-top: 0; }
.grade-text__inner > *:last-child { margin-bottom: 0; }

/* ---------- Image ---------- */
.grade-image__figure { margin: 0; display: flex; flex-direction: column; }
.grade-image__img, .grade-el--image img { max-width: 100%; height: auto; display: block; transition: transform .35s ease, opacity .3s ease; }
.grade-image__caption { font-size: .875em; color: var(--grade-muted); margin-top: .5em; }
/* An empty image element. Drawn here rather than with an icon font, so a page
   that merely contains an image does not have to load one. */
.grade-image__placeholder {
	display: block;
	width: 64px;
	height: 64px;
	border: 2px solid var(--grade-border, #d6dae0);
	border-radius: 4px;
	position: relative;
	opacity: .6;
}
.grade-image__placeholder::before {
	content: "";
	position: absolute;
	left: 12%;
	right: 12%;
	bottom: 18%;
	height: 34%;
	border-top: 2px solid var(--grade-border, #d6dae0);
	transform: skewY(-14deg);
}
.grade-image__placeholder::after {
	content: "";
	position: absolute;
	top: 18%;
	right: 18%;
	width: 12%;
	padding-top: 12%;
	border-radius: 50%;
	background: var(--grade-border, #d6dae0);
}
.grade-el--image { overflow: hidden; }

/* ---------- Button ---------- */
/*
 * The wrapper holds one inline-flex button, so without this it also holds a
 * text line box sized by the theme's body line-height - and the element ends up
 * taller than the button inside it, for no reason the settings can explain. The
 * button sets its own line-height, so collapsing the wrapper's is safe.
 */
.grade-el--button { line-height: 0; }

.grade-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: .5em;
	border: 2px solid var(--grade-primary);
	background: var(--grade-primary);
	color: #fff;
	text-decoration: none;
	border-radius: var(--grade-radius);
	font-weight: 600;
	line-height: 1.2;
	cursor: pointer;
	transition: background-color .2s ease, color .2s ease, border-color .2s ease, transform .15s ease;
}
.grade-button:hover { transform: translateY(-1px); }
.grade-button--small  { padding: .5em 1em;    font-size: .875rem; }
.grade-button--medium { padding: .75em 1.5em; font-size: 1rem; }
.grade-button--large  { padding: .9em 2em;    font-size: 1.125rem; }
.grade-button--xlarge { padding: 1.1em 2.5em; font-size: 1.25rem; }
.grade-button--outline { background: transparent; color: var(--grade-primary); }
.grade-button--outline:hover { background: var(--grade-primary); color: #fff; }
.grade-button--text { background: transparent; border-color: transparent; color: var(--grade-primary); padding-left: 0; padding-right: 0; }
.grade-button--block { width: 100%; }
.grade-button .grade-button__icon { font-size: 1em; width: 1em; height: 1em; line-height: 1; }

/* ---------- Icon ---------- */
.grade-icon__wrap { display: inline-flex; align-items: center; justify-content: center; }
.grade-icon__wrap--circle { border-radius: 50%; padding: .5em; }
.grade-icon__wrap--square { border-radius: var(--grade-radius); padding: .5em; }
.grade-icon__wrap .grade-icon__i { display: inline-flex; align-items: center; justify-content: center; color: var(--grade-primary); font-size: 48px; width: 48px; height: 48px; transition: color .2s ease; }

/* ---------- Separator / spacer ---------- */
.grade-separator { display: flex; width: 100%; justify-content: center; }
.grade-separator__line { display: block; width: 100%; border-top: 1px solid var(--grade-border); }
.grade-spacer { width: 100%; height: 50px; }

/* ---------- List ---------- */
.grade-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.grade-list--inline { flex-direction: row; flex-wrap: wrap; }
.grade-list__item { display: flex; align-items: flex-start; gap: .6em; }
.grade-list .grade-list__icon { color: var(--grade-primary); flex: none; font-size: 1.1em; width: 1.1em; height: 1.1em; line-height: 1.4; }
.grade-list__item a { text-decoration: none; color: inherit; }

/* ---------- Video ---------- */
.grade-video { position: relative; width: 100%; background: #000; }
.grade-video--16-9 { aspect-ratio: 16 / 9; }
.grade-video--4-3  { aspect-ratio: 4 / 3; }
.grade-video--21-9 { aspect-ratio: 21 / 9; }
.grade-video--1-1  { aspect-ratio: 1 / 1; }
.grade-video--9-16 { aspect-ratio: 9 / 16; }
.grade-video iframe, .grade-video video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.grade-video__placeholder { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #999; font-size: .9rem; }

/* ---------- Accordion ---------- */
.grade-accordion__item { border: 1px solid var(--grade-border); border-radius: var(--grade-radius); margin-bottom: 8px; overflow: hidden; }
.grade-accordion__title {
	display: flex; align-items: center; justify-content: space-between; gap: 1em;
	width: 100%; padding: 1em 1.25em; background: transparent; border: 0;
	font: inherit; font-weight: 600; text-align: left; cursor: pointer; color: var(--grade-heading);
}
.grade-accordion__title:hover { background: rgba(0,0,0,.02); }
.grade-accordion .grade-accordion__icon { flex: none; font-size: 20px; width: 20px; height: 20px; transition: transform .2s ease; }
.grade-accordion__content { padding: 0 1.25em 1.25em; }
.grade-accordion__content > *:first-child { margin-top: 0; }

/* ---------- Tabs ---------- */
.grade-tabs--horizontal .grade-tabs__nav { display: flex; flex-wrap: wrap; gap: 4px; border-bottom: 1px solid var(--grade-border); }
.grade-tabs--vertical { display: grid; grid-template-columns: minmax(140px, 24%) 1fr; gap: 1.5rem; }
.grade-tabs--vertical .grade-tabs__nav { display: flex; flex-direction: column; gap: 4px; }
.grade-tabs__tab {
	display: inline-flex; align-items: center; gap: .5em;
	padding: .75em 1.25em; background: transparent; border: 0; border-bottom: 2px solid transparent;
	font: inherit; font-weight: 600; cursor: pointer; color: var(--grade-muted);
}
.grade-tabs__tab.is-active { color: var(--grade-primary); border-bottom-color: var(--grade-primary); }
.grade-tabs--vertical .grade-tabs__tab { border-bottom: 0; border-left: 2px solid transparent; text-align: left; }
.grade-tabs--vertical .grade-tabs__tab.is-active { border-left-color: var(--grade-primary); }
.grade-tabs__panel { padding: 1.25em 0; }
.grade-tabs__panel > *:first-child { margin-top: 0; }

/* ---------- Counter ---------- */
.grade-counter__number { font-size: 3rem; font-weight: 700; line-height: 1.1; color: var(--grade-heading); }
.grade-counter__title { color: var(--grade-muted); margin-top: .35em; }

/* ---------- Testimonials ---------- */
.grade-testimonials { display: grid; grid-template-columns: 1fr; gap: 24px; }
.grade-testimonial { margin: 0; padding: 1.75rem; background: var(--grade-surface); border: 1px solid var(--grade-border); border-radius: var(--grade-radius); }
.grade-testimonial__stars { color: #f5a623; display: flex; gap: 2px; margin-bottom: .75em; }
.grade-testimonial .grade-testimonial__stars .dashicons { font-size: 18px; width: 18px; height: 18px; }
.grade-testimonial__quote { margin: 0 0 1.25em; color: var(--grade-text); font-size: 1.0625rem; line-height: 1.6; }
.grade-testimonial__meta { display: flex; align-items: center; gap: .85em; }
.grade-testimonial__avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; flex: none; }
.grade-testimonial__person { display: flex; flex-direction: column; }
.grade-testimonial__name { font-weight: 600; color: var(--grade-heading); }
.grade-testimonial__role { font-size: .875em; color: var(--grade-muted); }

/* ---------- Gallery ---------- */
.grade-gallery { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 12px; }
.grade-gallery img { width: 100%; display: block; transition: opacity .25s ease, transform .35s ease; }
.grade-gallery--empty { display: block; padding: 2rem; text-align: center; color: var(--grade-muted); border: 1px dashed var(--grade-border); border-radius: var(--grade-radius); }

/* ---------- Posts ---------- */
.grade-posts { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 24px; }
.grade-posts--empty { display: block; padding: 2rem; text-align: center; color: var(--grade-muted); }
.grade-post { background: var(--grade-surface); border: 1px solid var(--grade-border); border-radius: var(--grade-radius); overflow: hidden; display: flex; flex-direction: column; }
.grade-post__thumb { display: block; }
.grade-post__thumb img { width: 100%; height: 200px; object-fit: cover; display: block; }
.grade-post__body { padding: 1.25rem; display: flex; flex-direction: column; gap: .6em; flex: 1; }
.grade-post__title { margin: 0; font-size: 1.15rem; line-height: 1.35; }
.grade-post__title a { color: var(--grade-heading); text-decoration: none; }
.grade-post__title a:hover { color: var(--grade-primary); }
.grade-post__meta { font-size: .8125rem; color: var(--grade-muted); display: flex; gap: .5em; }
.grade-post__excerpt { margin: 0; color: var(--grade-text); font-size: .9375rem; line-height: 1.6; }
.grade-post__more { margin-top: auto; color: var(--grade-primary); font-weight: 600; text-decoration: none; font-size: .9375rem; }

/* ---------- Logo & nav ---------- */
.grade-logo__link { text-decoration: none; display: inline-flex; align-items: center; }
.grade-logo img { display: block; height: auto; }
.grade-logo__text { font-size: 1.5rem; font-weight: 700; color: var(--grade-heading); }

.grade-nav__list { list-style: none; margin: 0; padding: 0; display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.grade-nav--vertical .grade-nav__list { flex-direction: column; align-items: flex-start; }
.grade-nav a { text-decoration: none; color: var(--grade-text); font-weight: 500; transition: color .2s ease; }
.grade-nav a:hover, .grade-nav .current-menu-item > a { color: var(--grade-primary); }
/*
 * A horizontal nav item is a flex box around a link, not a paragraph. Left as a
 * list-item it keeps the theme's body font-size and line-height, so the row's
 * height is set by text nobody can see - and any typography you apply to the
 * link cannot bring it back down. That made a 77px header render at 80px with
 * no setting able to close the gap.
 */
.grade-nav__list > li { display: flex; align-items: center; line-height: 1; font-size: inherit; }

/* Separators sit on the list item, not between elements, so a menu whose
   items wrap does not leave a stray mark at the end of a line. */
.grade-nav--sep-slash .grade-nav__list > li,
.grade-nav--sep-slash-back .grade-nav__list > li,
.grade-nav--sep-line .grade-nav__list > li,
.grade-nav--sep-dot .grade-nav__list > li { position: relative; }

.grade-nav--sep-slash .grade-nav__list > li + li::before,
.grade-nav--sep-slash-back .grade-nav__list > li + li::before,
.grade-nav--sep-line .grade-nav__list > li + li::before,
.grade-nav--sep-dot .grade-nav__list > li + li::before {
	content: "";
	position: absolute;
	left: calc(var(--grade-nav-gap, 24px) / -2);
	top: 50%;
	background: currentColor;
	opacity: .5;
	pointer-events: none;
}

/*
 * Which way the line leans is a real design decision, not a detail - a menu
 * whose separators lean the opposite way to the rest of the identity reads as
 * a mistake. Both directions are offered rather than one being assumed.
 */
.grade-nav--sep-slash .grade-nav__list > li + li::before {
	width: 1.5px;
	height: 1.6em;
	transform: translate(-50%, -50%) rotate(45deg);
}

.grade-nav--sep-slash-back .grade-nav__list > li + li::before {
	width: 1.5px;
	height: 1.6em;
	transform: translate(-50%, -50%) rotate(-45deg);
}

.grade-nav--sep-line .grade-nav__list > li + li::before {
	width: 1px;
	height: 1.2em;
	transform: translate(-50%, -50%);
}

.grade-nav--sep-dot .grade-nav__list > li + li::before {
	width: 3px;
	height: 3px;
	border-radius: 50%;
	transform: translate(-50%, -50%);
}

.grade-nav--vertical .grade-nav__list > li + li::before { display: none; }
.grade-nav--vertical .grade-nav__list > li { width: 100%; }
.grade-nav li { position: relative; }
.grade-nav .sub-menu {
	list-style: none; margin: 0; padding: .5em 0; position: absolute; top: 100%; left: 0; min-width: 200px;
	background: var(--grade-surface); border: 1px solid var(--grade-border); border-radius: var(--grade-radius);
	box-shadow: 0 8px 24px rgba(0,0,0,.08); opacity: 0; visibility: hidden; transform: translateY(6px);
	transition: opacity .2s ease, transform .2s ease, visibility .2s; z-index: 50;
}
.grade-nav li:hover > .sub-menu, .grade-nav li:focus-within > .sub-menu { opacity: 1; visibility: visible; transform: translateY(0); }
.grade-nav .sub-menu a { display: block; padding: .5em 1.15em; }
.grade-nav .sub-menu .sub-menu { top: 0; left: 100%; }
.grade-nav__toggle { display: none; background: transparent; border: 0; cursor: pointer; padding: 8px; }
.grade-nav__bars, .grade-nav__bars::before, .grade-nav__bars::after {
	display: block; width: 22px; height: 2px; background: currentColor; position: relative; transition: transform .2s ease;
}
.grade-nav__bars::before, .grade-nav__bars::after { content: ''; position: absolute; left: 0; }
.grade-nav__bars::before { top: -7px; }
.grade-nav__bars::after  { top: 7px; }
.grade-nav--empty { color: var(--grade-muted); font-size: .9rem; padding: .5em 0; }

@media (max-width: 767px) {
	.grade-nav__toggle { display: block; }
	.grade-nav .grade-nav__list { display: none; flex-direction: column; align-items: flex-start; width: 100%; padding: .75em 0; }

	/*
	 * Full width, hanging off the bottom of the header - see positionPanel() in
	 * frontend.js for where the top comes from. The fallback matters: if the
	 * script has not run the panel still opens somewhere sensible rather than
	 * on top of the logo.
	 */
	.grade-nav.is-open .grade-nav__list {
		display: flex;
		position: fixed;
		top: var(--grade-nav-panel-top, 56px);
		left: 0;
		right: 0;
		width: auto;
		z-index: 60;
		padding: 8px var(--grade-gutter, 24px) 20px;
		background: var(--grade-nav-panel-bg, var(--grade-surface, #fff));
		box-shadow: 0 18px 40px rgba(0, 0, 0, .28);
	}

	.grade-nav.is-open .grade-nav__list > li { width: 100%; }
	.grade-nav.is-open .grade-nav__list > li > a { display: block; width: 100%; padding: 10px 0; }
	.grade-nav .sub-menu { position: static; opacity: 1; visibility: visible; transform: none; box-shadow: none; border: 0; padding-left: 1em; }
}

/* ---------- Form ---------- */
.grade-form { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 16px; }
.grade-form__field { display: flex; flex-direction: column; gap: .4em; grid-column: span 6; }
.grade-form__field--w50 { grid-column: span 3; }
.grade-form__field--w33 { grid-column: span 2; }
.grade-form label { font-size: .875rem; font-weight: 600; color: var(--grade-heading); }
.grade-form input:not([type="checkbox"]), .grade-form textarea, .grade-form select {
	width: 100%; padding: .7em .9em; border: 1px solid var(--grade-border);
	border-radius: var(--grade-radius); font: inherit; color: var(--grade-text); background: #fff;
	transition: border-color .2s ease, box-shadow .2s ease;
}
/* The halo is a custom property rather than a literal, because the border it
   sits inside is already --grade-primary and the halo was Grade's stock blue
   nailed down: a site that repointed --grade-primary got a blue ring around
   every field in a palette with no blue in it, and nothing in the panel to
   move it. The fallback is the value it has always had, so a site that sets
   nothing is unchanged. */
.grade-form input:focus, .grade-form textarea:focus, .grade-form select:focus {
	outline: none; border-color: var(--grade-primary); box-shadow: 0 0 0 3px var(--grade-focus-ring, rgba(37,99,235,.12));
}
.grade-form__checkbox { display: flex; align-items: center; gap: .5em; font-weight: 400; }
/* A file input's own button is drawn by the browser and cannot be styled from
   here, so the field is given room around it rather than the tight padding a
   text box gets - at .7em the button sits hard against the border. */
.grade-form input[type="file"] { padding: .55em .6em; cursor: pointer; }
.grade-form input[type="file"]::file-selector-button {
	font: inherit; margin-right: .8em; padding: .35em .8em; cursor: pointer;
	border: 1px solid var(--grade-border); border-radius: var(--grade-radius);
	background: var(--grade-surface, #f6f6f8); color: inherit;
}
/* What the field will take, said before the file is chosen rather than after
   it is refused. */
.grade-form__note { font-size: .8125rem; color: var(--grade-muted, #6b7280); }
/* Same reason as the focus ring above: the required asterisk was a hardcoded
   red, so a brand with its own red - or no red at all - could not move it. The
   fallback is the value it has always had. */
.grade-form__req { color: var(--grade-danger, #dc2626); margin-left: .2em; }
.grade-form__actions { grid-column: span 6; }
.grade-form__hp { position: absolute !important; left: -9999px !important; height: 1px; overflow: hidden; }
.grade-form__message { grid-column: span 6; font-size: .9375rem; }
.grade-form__message.is-error { color: #dc2626; }
.grade-form__message.is-success { color: #15803d; }
.grade-form.is-sending { opacity: .6; pointer-events: none; }

/* A sent form collapses to its message - sent() in frontend.js puts `hidden` on
   every other child and .is-sent on the form.

   This rule exists because `[hidden]` is a UA rule worth one attribute, and
   .grade-form__field sets display:flex, so the field would stay on screen. It
   is written at a specificity a class rule cannot reach - the tag, the form's
   class, the state class and the attribute, on a direct child - rather than
   with !important, because a client styling their own form fields is the normal
   thing to do and !important here would switch that off. Same reasoning as the
   [hidden] rules on the stepped form's buttons below.

   The tag on the front is doing work, not decoration. Without it this ties with
   `.grade-form--stepped.is-stepped .grade-form__rail{display:block}` further
   down, and a later rule wins a tie - so a sent multi-step form collapsed to
   its message with the progress rail still sitting above it, numbering the
   steps of a form that was no longer there. Measured, not reasoned: the rail
   was still 100% wide and visible after a successful send. */
form.grade-form.is-sent > [hidden] { display: none; }
/* Nothing here removes the focus ring the browser draws on the message. Focus
   was moved to it by script rather than by a tab press, so the ring is not
   telling a keyboard user where they are - but it is telling them where they
   will be when they next press Tab, and a sighted keyboard user who cannot see
   where focus went is the failure this whole change was made to avoid. */

/* ---------- Multi-step forms ---------- */
/* Everything here defaults to the no-JavaScript page: every step on screen, no
   rail, no Back or Continue, and one submit that posts the lot. The stepping is
   added by .is-stepped, which the script puts on the form once it has taken
   charge of it. Written this way round on purpose - a form whose steps are
   hidden by the stylesheet and revealed by script is blank to anybody whose
   script did not arrive, and a blank application form loses applications
   without anybody finding out. */
.grade-form__step {
	grid-column: 1 / -1; min-width: 0; margin: 0; padding: 0; border: 0;
	display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
	/* The Field Spacing control writes gap onto .grade-form, so inherit it
	   rather than restating a number the control would then fail to move. */
	gap: inherit;
}
.grade-form__step[hidden] { display: none; }
.grade-form__legend { padding: 0; font-size: 1.125rem; font-weight: 600; color: var(--grade-heading); }
.grade-form__legend + .grade-form__note { grid-column: 1 / -1; margin: -.2em 0 0; }
.grade-form__step > .grade-form__note { grid-column: 1 / -1; }

.grade-form__rail { grid-column: 1 / -1; display: none; }
.grade-form__rail ol {
	display: flex; flex-wrap: wrap; gap: .4em 1.5em;
	margin: 0; padding: 0; list-style: none;
}
.grade-form__rail-item {
	display: flex; align-items: center; gap: .5em;
	font-size: .875rem; color: var(--grade-muted, #6b7280);
}
.grade-form__rail-num {
	display: inline-flex; align-items: center; justify-content: center; flex: none;
	width: 1.8em; height: 1.8em; border: 1px solid var(--grade-border); border-radius: 50%;
	font-size: .8125rem; transition: background-color .2s ease, color .2s ease;
}
/* Three states told apart by fill, weight and an underline as well as by
   colour, so the rail still reads on a monochrome screen or to somebody who
   cannot tell the two greys apart. */
.grade-form__rail-item.is-done,
.grade-form__rail-item.is-current { color: var(--grade-heading); }
.grade-form__rail-item.is-done .grade-form__rail-num,
.grade-form__rail-item.is-current .grade-form__rail-num {
	background: var(--grade-heading); border-color: var(--grade-heading); color: var(--grade-surface);
}
.grade-form__rail-item.is-current { font-weight: 600; }
.grade-form__rail-item.is-current .grade-form__rail-label { text-decoration: underline; text-underline-offset: 3px; }

.grade-form__back, .grade-form__next { display: none; }
.grade-form--stepped .grade-form__actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
/* The forward button sits at the far end whether or not Back is showing, so it
   does not jump across the row between step one and step two. */
.grade-form--stepped .grade-form__submit,
.grade-form--stepped .grade-form__next { margin-left: auto; }
.grade-form--stepped.is-stepped .grade-form__rail { display: block; }
.grade-form--stepped.is-stepped .grade-form__back:not([hidden]),
.grade-form--stepped.is-stepped .grade-form__next:not([hidden]) { display: inline-flex; }
/* Hiding a button is done with the `hidden` attribute, and the attribute has
   to be able to win.

   `[hidden]` is a plain UA rule at one specificity point, so ANY theme or
   snippet rule that sets display on the class beats it - and a client styling
   the submit button is not an edge case, it is the normal thing to do. The
   first site to do it got a "Send my application" button sitting on step 1 of
   4, next to Continue, posting a form the applicant had not filled in yet.

   So these are written at a specificity a class rule cannot reach, without
   reaching for !important: the stepped form's own class, plus the actions
   wrapper, plus the element, plus the attribute. */
.grade-form__submit[hidden] { display: none; }
.grade-form--stepped .grade-form__actions button[hidden],
.grade-form--stepped .grade-form__actions [hidden] { display: none; }

@media (max-width: 767px) {
	.grade-form__field--w50, .grade-form__field--w33 { grid-column: span 6; }
}

/* ---------- Post content ---------- */
.grade-post-content > *:first-child { margin-top: 0; }
.grade-post-content--empty { color: var(--grade-muted); font-style: italic; }

/* ---------- Animations ---------- */
[data-grade-anim] { opacity: 0; }
[data-grade-anim].grade-anim-in { animation-duration: var(--grade-anim-duration, 600ms); animation-fill-mode: both; animation-timing-function: cubic-bezier(.16,1,.3,1); opacity: 1; }
[data-grade-anim="fade-in"].grade-anim-in     { animation-name: gradeFadeIn; }
[data-grade-anim="slide-up"].grade-anim-in    { animation-name: gradeSlideUp; }
[data-grade-anim="slide-down"].grade-anim-in  { animation-name: gradeSlideDown; }
[data-grade-anim="slide-left"].grade-anim-in  { animation-name: gradeSlideLeft; }
[data-grade-anim="slide-right"].grade-anim-in { animation-name: gradeSlideRight; }
[data-grade-anim="zoom-in"].grade-anim-in     { animation-name: gradeZoomIn; }

@keyframes gradeFadeIn     { from { opacity: 0; }                              to { opacity: 1; } }
@keyframes gradeSlideUp    { from { opacity: 0; transform: translateY(40px); } to { opacity: 1; transform: none; } }
@keyframes gradeSlideDown  { from { opacity: 0; transform: translateY(-40px);} to { opacity: 1; transform: none; } }
@keyframes gradeSlideLeft  { from { opacity: 0; transform: translateX(40px); } to { opacity: 1; transform: none; } }
@keyframes gradeSlideRight { from { opacity: 0; transform: translateX(-40px);} to { opacity: 1; transform: none; } }
@keyframes gradeZoomIn     { from { opacity: 0; transform: scale(.9); }        to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
	[data-grade-anim] { opacity: 1 !important; animation: none !important; }
	.grade-button:hover { transform: none; }
	.grade-form__rail-num { transition: none; }
}

/* ---------- Lightbox ---------- */
.grade-lightbox-overlay {
	position: fixed; inset: 0; background: rgba(9,12,20,.92); z-index: 99999;
	display: flex; align-items: center; justify-content: center; padding: 32px;
	opacity: 0; transition: opacity .2s ease;
}
.grade-lightbox-overlay.is-visible { opacity: 1; }
.grade-lightbox-overlay img { max-width: 100%; max-height: 100%; display: block; border-radius: 4px; }
.grade-lightbox-close {
	position: absolute; top: 16px; right: 20px; background: transparent; border: 0;
	color: #fff; font-size: 32px; line-height: 1; cursor: pointer; padding: 8px;
}
.grade-lightbox-nav {
	position: absolute; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,.1);
	border: 0; color: #fff; font-size: 28px; cursor: pointer; padding: 12px 18px; border-radius: 4px;
}
.grade-lightbox-nav--prev { left: 20px; }
.grade-lightbox-nav--next { right: 20px; }

/* ================= Alert ================= */
.grade-alert {
	--grade-alert-accent: var(--grade-primary);
	display: flex; align-items: flex-start; gap: .9em;
	padding: 1em 1.2em; border-radius: var(--grade-radius); position: relative;
	background: color-mix(in srgb, var(--grade-alert-accent) 10%, transparent);
	color: var(--grade-text);
}
.grade-alert--success { --grade-alert-accent: #15803d; }
.grade-alert--warning { --grade-alert-accent: #b45309; }
.grade-alert--error   { --grade-alert-accent: #dc2626; }
.grade-alert--solid { background: var(--grade-alert-accent); color: #fff; }
.grade-alert--solid .grade-alert__icon { color: #fff; }
.grade-alert--left { background: color-mix(in srgb, var(--grade-alert-accent) 7%, transparent); border-left: 4px solid var(--grade-alert-accent); }
.grade-alert .grade-alert__icon { color: var(--grade-alert-accent); flex: none; font-size: 20px; width: 20px; height: 20px; }
.grade-alert__body { flex: 1; min-width: 0; }
.grade-alert__title { margin: 0 0 .25em; font-weight: 700; }
.grade-alert__text > *:first-child { margin-top: 0; }
.grade-alert__text > *:last-child { margin-bottom: 0; }
.grade-alert__close { background: none; border: 0; color: inherit; opacity: .6; font-size: 20px; line-height: 1; cursor: pointer; padding: 0 2px; }
.grade-alert__close:hover { opacity: 1; }
.grade-alert.is-dismissed { display: none; }

/* ================= Tagline box ================= */
.grade-tagline {
	display: flex; align-items: center; gap: 1.5em; flex-wrap: wrap;
	padding: 2em 2.2em; background: rgba(0,0,0,.03); border-radius: var(--grade-radius);
}
.grade-tagline--stacked { flex-direction: column; align-items: flex-start; }
.grade-tagline--centred { flex-direction: column; align-items: center; text-align: center; }
.grade-tagline__body { flex: 1; min-width: 240px; }
.grade-tagline__title { margin: 0; font-size: 1.5rem; font-weight: 700; color: var(--grade-heading); line-height: 1.25; }
.grade-tagline__text { margin: .4em 0 0; color: var(--grade-muted); }
.grade-tagline__btn {
	display: inline-flex; align-items: center; justify-content: center; flex: none;
	padding: .8em 1.7em; background: var(--grade-primary); border: 2px solid var(--grade-primary);
	color: #fff; border-radius: var(--grade-radius); text-decoration: none; font-weight: 600;
	transition: background .2s ease, transform .15s ease;
}
.grade-tagline__btn:hover { background: var(--grade-secondary); border-color: var(--grade-secondary); color: #fff; transform: translateY(-1px); }

/* ================= Content boxes ================= */
.grade-boxes { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 24px; }
.grade-box {
	display: flex; flex-direction: column; gap: .9em; padding: 1.75rem;
	background: var(--grade-surface); border-radius: var(--grade-radius);
	text-decoration: none; color: inherit;
	transition: transform .2s ease, box-shadow .2s ease;
}
.grade-boxes--lift .grade-box:hover { transform: translateY(-3px); box-shadow: 0 14px 32px rgba(0,0,0,.09); }
.grade-boxes--left .grade-box { flex-direction: row; align-items: flex-start; }
.grade-box__iconwrap { display: inline-flex; align-items: center; justify-content: center; flex: none; align-self: flex-start; }
.grade-box__iconwrap--circle { padding: .7em; border-radius: 50%; background: rgba(0,0,0,.05); }
.grade-box__iconwrap--square { padding: .7em; border-radius: var(--grade-radius); background: rgba(0,0,0,.05); }
.grade-box__iconwrap .grade-box__icon { color: var(--grade-primary); font-size: 34px; width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center; }
.grade-box__media img { border-radius: var(--grade-radius); width: 100%; }
.grade-box__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .5em; }
.grade-box__title { margin: 0; font-size: 1.15rem; color: var(--grade-heading); }
.grade-box__text { margin: 0; color: var(--grade-text); font-size: .9375rem; line-height: 1.6; }
.grade-box__link { align-self: flex-start; font-weight: 600; text-decoration: none; color: var(--grade-primary); }
.grade-box__link--button {
	padding: .55em 1.2em; border: 2px solid var(--grade-primary); border-radius: var(--grade-radius);
}
.grade-box__link--button:hover { background: var(--grade-primary); color: #fff; }

/* ================= Pricing ================= */
.grade-pricing { --grade-plan-accent: var(--grade-primary); display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 20px; align-items: start; }
.grade-plan {
	position: relative; display: flex; flex-direction: column; gap: .35em; padding: 2rem 1.75rem;
	background: var(--grade-surface); border: 1px solid var(--grade-border);
	border-radius: var(--grade-radius); text-align: center;
}
.grade-plan.is-featured { border-color: var(--grade-plan-accent); border-width: 2px; box-shadow: 0 16px 40px rgba(0,0,0,.10); }
.grade-plan__badge {
	position: absolute; top: 0; left: 50%; transform: translate(-50%,-50%);
	padding: .35em 1em; background: var(--grade-plan-accent); color: #fff;
	border-radius: 100px; font-size: .75rem; font-weight: 700; letter-spacing: .03em; white-space: nowrap;
}
.grade-plan__name { margin: 0; font-size: 1.05rem; text-transform: uppercase; letter-spacing: .06em; color: var(--grade-muted); }
.grade-plan__price { margin: .3em 0 0; display: flex; align-items: flex-start; justify-content: center; gap: .1em; color: var(--grade-heading); }
.grade-plan__currency { font-size: 1.4rem; font-weight: 600; margin-top: .35em; }
.grade-plan__amount { font-size: 3rem; font-weight: 800; line-height: 1; }
.grade-plan__period { margin: 0; color: var(--grade-muted); font-size: .875rem; }
.grade-plan__desc { margin: .8em 0 0; color: var(--grade-text); font-size: .9375rem; }
.grade-plan__features { list-style: none; margin: 1.4em 0; padding: 0; display: flex; flex-direction: column; gap: .6em; text-align: left; }
.grade-plan__features li { display: flex; align-items: flex-start; gap: .55em; font-size: .9375rem; }
.grade-plan__features .dashicons { color: var(--grade-plan-accent); font-size: 18px; width: 18px; height: 18px; flex: none; }
.grade-plan__btn {
	margin-top: auto; display: inline-flex; align-items: center; justify-content: center;
	padding: .8em 1.5em; background: var(--grade-plan-accent); border: 2px solid var(--grade-plan-accent);
	color: #fff; border-radius: var(--grade-radius); text-decoration: none; font-weight: 600;
}
.grade-plan:not(.is-featured) .grade-plan__btn { background: transparent; color: var(--grade-plan-accent); }
.grade-plan:not(.is-featured) .grade-plan__btn:hover { background: var(--grade-plan-accent); color: #fff; }

/* ================= Flip boxes ================= */
.grade-flips { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 20px; }
.grade-flip { height: 260px; perspective: 1400px; outline: none; }
.grade-flip__inner {
	position: relative; width: 100%; height: 100%;
	transition: transform .6s cubic-bezier(.4,.2,.2,1); transform-style: preserve-3d;
}
.grade-flip:hover .grade-flip__inner,
.grade-flip:focus .grade-flip__inner,
.grade-flip.is-flipped .grade-flip__inner { transform: var(--grade-flip-transform, rotateY(180deg)); }
.grade-flip--left  { --grade-flip-transform: rotateY(180deg); }
.grade-flip--right { --grade-flip-transform: rotateY(-180deg); }
.grade-flip--up    { --grade-flip-transform: rotateX(180deg); }
.grade-flip--down  { --grade-flip-transform: rotateX(-180deg); }
.grade-flip__face {
	position: absolute; inset: 0; display: flex; flex-direction: column;
	align-items: center; justify-content: center; gap: .6em; padding: 1.75rem;
	backface-visibility: hidden; -webkit-backface-visibility: hidden;
	border-radius: var(--grade-radius); text-align: center;
}
.grade-flip__front { background: var(--grade-surface); border: 1px solid var(--grade-border); color: var(--grade-text); }
.grade-flip__back { background: var(--grade-primary); color: #fff; transform: var(--grade-flip-transform, rotateY(180deg)); }
.grade-flip .grade-flip__icon { font-size: 40px; width: 40px; height: 40px; color: var(--grade-primary); }
.grade-flip__title { margin: 0; font-size: 1.2rem; color: inherit; }
.grade-flip__text { margin: 0; font-size: .9375rem; line-height: 1.6; }
.grade-flip__link { color: inherit; font-weight: 600; text-decoration: underline; }

/* ================= Progress ================= */
.grade-progresses { display: flex; flex-direction: column; gap: 18px; }
.grade-progress__head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: .45em; }
.grade-progress__label { font-weight: 600; color: var(--grade-heading); font-size: .9375rem; }
.grade-progress__value { color: var(--grade-muted); font-size: .875rem; }
.grade-progress__track { width: 100%; height: 12px; background: rgba(0,0,0,.07); border-radius: 100px; overflow: hidden; }
.grade-progress__fill {
	height: 100%; background: var(--grade-primary); border-radius: 100px;
	transition: width 1.1s cubic-bezier(.16,1,.3,1);
	display: flex; align-items: center; justify-content: flex-end;
}
.grade-progresses--inside .grade-progress__track { height: 26px; }
.grade-progress__inside { padding: 0 .8em; color: #fff; font-size: .8125rem; font-weight: 600; white-space: nowrap; }

/* ================= Person ================= */
.grade-people { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 24px; }
.grade-person { display: flex; flex-direction: column; gap: .4em; text-align: center; }
.grade-person__photo { overflow: hidden; border-radius: var(--grade-radius); margin-bottom: .6em; }
.grade-person__photo--circle { border-radius: 50%; aspect-ratio: 1; }
.grade-person__photo--circle img { width: 100%; height: 100%; object-fit: cover; }
.grade-person__photo img { width: 100%; display: block; }
.grade-person__name { margin: 0; font-size: 1.1rem; color: var(--grade-heading); }
.grade-person__role { margin: 0; color: var(--grade-muted); font-size: .9375rem; }
.grade-person__bio { margin: .5em 0 0; font-size: .9375rem; line-height: 1.6; }
.grade-person__social { display: flex; gap: .5em; margin-top: .6em; justify-content: inherit; }
.grade-person__social a { color: var(--grade-muted); text-decoration: none; }
.grade-person__social a:hover { color: var(--grade-primary); }

/* ================= Social links ================= */
.grade-social { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.grade-social__link {
	display: inline-flex; align-items: center; justify-content: center;
	width: 40px; height: 40px; color: var(--grade-text);
	background: rgba(0,0,0,.05); text-decoration: none;
	transition: background .2s ease, color .2s ease, transform .15s ease;
}
.grade-social--circle .grade-social__link { border-radius: 50%; }
.grade-social--square .grade-social__link { border-radius: var(--grade-radius); }
.grade-social--none .grade-social__link { background: transparent; width: auto; height: auto; }
.grade-social__link:hover { transform: translateY(-2px); color: var(--grade-primary); }

/* ================= Map ================= */
.grade-map { position: relative; width: 100%; height: 380px; background: rgba(0,0,0,.04); border-radius: var(--grade-radius); overflow: hidden; }
.grade-map iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.grade-map--empty { display: flex; align-items: center; justify-content: center; color: var(--grade-muted); font-size: .9rem; }

/* ================= Countdown ================= */
.grade-countdown { display: flex; flex-wrap: wrap; gap: 12px; align-items: stretch; }
.grade-countdown__unit {
	display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .2em;
	min-width: 78px; padding: 1em .9em; background: rgba(0,0,0,.04); border-radius: var(--grade-radius);
}
.grade-countdown__number { font-size: 2rem; font-weight: 800; line-height: 1; color: var(--grade-heading); font-variant-numeric: tabular-nums; }
.grade-countdown__label { font-size: .75rem; text-transform: uppercase; letter-spacing: .06em; color: var(--grade-muted); }
.grade-countdown__done { font-weight: 600; color: var(--grade-heading); }
.grade-countdown--empty { color: var(--grade-muted); font-size: .9rem; }

/* ================= Table ================= */
.grade-table__scroll { overflow-x: auto; }
.grade-table { width: 100%; border-collapse: collapse; margin: 0; }
.grade-table caption { caption-side: bottom; padding-top: .8em; color: var(--grade-muted); font-size: .875rem; text-align: left; }
.grade-table th, .grade-table td { padding: .8em 1em; text-align: left; border: 0; }
.grade-table--bordered th, .grade-table--bordered td { border: 1px solid var(--grade-border); }
.grade-table thead th { background: rgba(0,0,0,.04); font-weight: 700; color: var(--grade-heading); }
.grade-table--striped tbody tr:nth-child(even) { background: rgba(0,0,0,.02); }
.grade-table--firstbold tbody th { font-weight: 700; color: var(--grade-heading); }
.grade-table tbody th { font-weight: 400; }

/* ================= Rating ================= */
.grade-rating { display: flex; align-items: center; gap: .6em; flex-wrap: wrap; }
.grade-rating__stars { display: inline-flex; color: #f5a623; }
.grade-rating__stars .dashicons { font-size: 20px; width: 20px; height: 20px; }
.grade-rating__star--empty { color: var(--grade-border); }
.grade-rating__text { color: var(--grade-muted); font-size: .9375rem; }

/* ================= Anchor ================= */
.grade-anchor { display: block; height: 0; scroll-margin-top: 0; }
.grade-anchor--empty, .grade-anchor__hint {
	display: inline-block; padding: 2px 8px; background: rgba(120,130,160,.12);
	border-radius: 3px; font-size: 11px; color: #6b7280; font-family: Menlo, Consolas, monospace;
}

@media (max-width: 900px) {
	.grade-boxes, .grade-pricing, .grade-flips, .grade-people { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 640px) {
	.grade-boxes, .grade-pricing, .grade-flips, .grade-people { grid-template-columns: minmax(0,1fr); }
	.grade-tagline { flex-direction: column; align-items: flex-start; }
}
@media (prefers-reduced-motion: reduce) {
	.grade-flip__inner { transition: none; }
	.grade-progress__fill { transition: none; }
	.grade-box:hover, .grade-social__link:hover, .grade-tagline__btn:hover { transform: none; }
}

/* ---------- Eyebrow ---------- */

.grade-eyebrow { display: flex; align-items: center; gap: 12px; font-size: 11px; letter-spacing: .26em; text-transform: uppercase; line-height: 1.4; }
.grade-eyebrow__marker { flex: none; width: 5px; height: 5px; background: currentColor; }
.grade-eyebrow--rule .grade-eyebrow__marker { width: 26px; height: 1px; }
.grade-eyebrow__suffix { opacity: .7; }

/* ---------- Vector ---------- */

.grade-vector__wrap { display: flex; }
.grade-vector { display: inline-block; line-height: 0; }
.grade-vector svg { display: block; width: 100%; height: auto; overflow: visible; }
.grade-vector__link { display: inline-block; line-height: 0; }
.grade-vector__empty {
	padding: 18px; border: 1px dashed rgba(127,127,127,.5); border-radius: 6px;
	font-size: 13px; text-align: center; opacity: .7;
}

/* ---------- Index rows ---------- */

.grade-irows { --grade-irow-rule: rgba(127,127,127,.22); --grade-irow-meta: 200px; }
.grade-irows--rule-both { border-top: 1px solid var(--grade-irow-rule); }
.grade-irow { display: grid; gap: 28px; align-items: start; padding: 34px 4px; text-decoration: none; color: inherit; }
.grade-irows--rule-both .grade-irow,
.grade-irows--rule-bottom .grade-irow { border-bottom: 1px solid var(--grade-irow-rule); }

.grade-irows--stack .grade-irow { grid-template-columns: 110px 1fr var(--grade-irow-meta); }
.grade-irows--inline .grade-irow { grid-template-columns: 1fr auto; align-items: center; padding: 26px 4px; }

.grade-irow__mark { display: block; width: 56px; }
.grade-irow__mark svg { display: block; width: 100%; height: auto; }
.grade-irow__mark--icon .dashicons { font-size: 40px; width: 40px; height: 40px; }

.grade-irow__body { display: flex; flex-direction: column; min-width: 0; }
.grade-irows--inline .grade-irow__body { flex-direction: row; align-items: baseline; gap: 20px; flex-wrap: wrap; }

.grade-irow__title { font-size: clamp(24px, 2.6vw, 34px); font-weight: 600; letter-spacing: -.03em; line-height: 1.05; }
.grade-irow__text { margin-top: 12px; font-size: 15.5px; line-height: 1.6; opacity: .7; max-width: 52ch; }
.grade-irows--inline .grade-irow__text { margin-top: 0; }
.grade-irow__aside,
.grade-irow__meta { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; opacity: .55; }
.grade-irows--stack .grade-irow__meta { text-align: right; padding-top: 8px; }

.grade-irows--hover .grade-irow--link { transition: padding .25s, background .25s; }
.grade-irows--hover .grade-irow--link:hover { padding-left: 16px; background: linear-gradient(90deg, rgba(127,127,127,.08), transparent); }
.grade-irow--muted { pointer-events: none; }
.grade-irow--muted .grade-irow__title { opacity: .35; }

.grade-irows__empty { padding: 18px; border: 1px dashed rgba(127,127,127,.5); border-radius: 6px; font-size: 13px; text-align: center; opacity: .7; }

@media (max-width: 760px) {
	.grade-irows--stack .grade-irow { grid-template-columns: 70px 1fr; gap: 18px; }
	.grade-irows--stack .grade-irow__meta { display: none; }
	.grade-irows--inline .grade-irow { grid-template-columns: 1fr; gap: 10px; }
}

/* ---------- Pricing: rail layout ---------- */

.grade-pricing--rail { gap: 0; }
.grade-plan--rail {
	display: flex; flex-direction: column; align-items: flex-start;
	padding: 30px 28px; border-left: 1px solid rgba(127,127,127,.22);
	background: none; border-radius: 0; text-align: left;
	text-decoration: none; color: inherit; transition: background .2s;
}
.grade-plan--rail:first-child { border-left: none; }
.grade-plan--rail:hover { background: rgba(127,127,127,.06); }
.grade-plan--rail.is-featured { background: linear-gradient(180deg, rgba(127,127,127,.09), transparent); }

.grade-plan__mark { display: block; width: 46px; margin-bottom: 18px; }
.grade-plan__mark svg { display: block; width: 100%; height: auto; }
.grade-plan__tier { font-size: 11px; letter-spacing: .16em; text-transform: uppercase; opacity: .5; }
.grade-plan--rail .grade-plan__name { margin-top: 12px; font-size: 26px; font-weight: 600; letter-spacing: -.02em; }
.grade-plan__descriptor { margin-top: 2px; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; }
.grade-plan--rail .grade-plan__price { margin-top: 18px; font-size: 32px; font-weight: 600; letter-spacing: -.03em; }
.grade-plan__suffix { font-size: 13px; opacity: .6; font-weight: 400; letter-spacing: 0; }

.grade-plan--rail .grade-plan__features {
	display: flex; flex-direction: column; gap: 11px; width: 100%;
	margin-top: 18px; padding-top: 16px; border-top: 1px solid rgba(127,127,127,.16);
}
.grade-plan__feature { display: flex; gap: 10px; font-size: 13.5px; line-height: 1.4; opacity: .7; }
.grade-plan__bullet { opacity: .55; }
.grade-plan__more { margin-top: 16px; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; opacity: .5; transition: opacity .2s, color .2s; }
.grade-plan--rail:hover .grade-plan__more { opacity: 1; }

@media (max-width: 820px) {
	.grade-plan--rail { border-left: none; border-top: 1px solid rgba(127,127,127,.22); }
	.grade-plan--rail:first-child { border-top: none; }
}

/* ---------- Header slot behaviour ---------- */
/*
 * A builder header replaces the theme's own <header>, so the sticky, shrink and
 * transparent behaviours have to be honoured on the slot as well - otherwise
 * those switches quietly stop working the moment anyone assigns a Header
 * layout, which is the normal way to use the builder.
 */
.grade-slot--header.is-sticky { position: sticky; top: 0; z-index: 100; }
.grade-slot--header.is-transparent { position: absolute; left: 0; right: 0; z-index: 100; }

/*
 * A header that is not transparent must not be see-through.
 *
 * A header built for a transparent design carries no background of its own -
 * that is the point of it. The moment it stops being transparent, whether from
 * the setting or because the page has no backdrop, it is white text on whatever
 * the page happens to be. This is the floor, and only a floor: any background
 * the design puts on the header container itself paints over it.
 */
.grade-slot--header:not(.is-transparent) {
	background-color: var(--grade-header-bg, transparent);
}
.grade-slot--header.is-transparent.is-scrolled { position: fixed; }
body.admin-bar .grade-slot--header.is-sticky,
body.admin-bar .grade-slot--header.is-transparent { top: 32px; }
@media (max-width: 782px) {
	body.admin-bar .grade-slot--header.is-sticky,
	body.admin-bar .grade-slot--header.is-transparent { top: 46px; }
}
.grade-slot--header.is-shrink { transition: box-shadow .2s ease; }
.grade-slot--header.is-shrink.is-scrolled { box-shadow: 0 2px 16px rgba(0,0,0,.10); }

/* A builder header that has been scrolled past. The theme's own header does
   this from the same variables; these rules give a header built out of
   containers the same behaviour, which it otherwise has no way to reach. */
.grade-slot--header.is-solid-on-scroll { transition: background-color .25s ease, backdrop-filter .25s ease; }

.grade-slot--header.is-solid-on-scroll.is-scrolled {
	background-color: var(--grade-header-sticky-bg, var(--grade-header-bg, transparent));
	backdrop-filter: blur(var(--grade-header-sticky-blur, 0px));
	-webkit-backdrop-filter: blur(var(--grade-header-sticky-blur, 0px));
}

/* Shrinking applies to the container inside, because the slot itself has no
   height of its own - it is only a wrapper. */
.grade-slot--header.is-shrink > .grade-container > .grade-container__inner { transition: min-height .25s ease; }
.grade-slot--header.is-shrink.is-scrolled > .grade-container > .grade-container__inner { min-height: var(--grade-header-sticky-height, 60px); }

@media (prefers-reduced-motion: reduce) {
	.grade-slot--header.is-solid-on-scroll,
	.grade-slot--header.is-shrink > .grade-container > .grade-container__inner { transition: none; }
}

/* ---------- Builder preview pane ---------- */
/*
 * Selecting a row in the builder scrolls the preview to that element and
 * flashes it. Without the outline you land somewhere on a long page with no
 * idea which of the things on screen you just clicked.
 */
.grade-preview-flash {
	animation: grade-preview-flash 1.2s ease-out;
	outline: 2px solid rgba(34, 113, 177, .9);
	outline-offset: 2px;
}

@keyframes grade-preview-flash {
	0%   { outline-color: rgba(34, 113, 177, .9); }
	70%  { outline-color: rgba(34, 113, 177, .9); }
	100% { outline-color: rgba(34, 113, 177, 0); }
}

@media (prefers-reduced-motion: reduce) {
	.grade-preview-flash { animation: none; }
}

/* The preview pane is a page in a small window - it should never grow its own
   horizontal scrollbar just because the frame is narrow. */
body.grade-preview-pane { overflow-x: hidden; }

/*
 * Entrance animations start at opacity 0 and play when the element scrolls
 * into view. In a preview that is re-rendered on every keystroke that means
 * half the page sits invisible while you are trying to look at it - and the
 * observer never fires for anything already on screen after a swap. In the
 * pane, everything is simply arrived.
 */
body.grade-preview-pane [data-grade-anim] {
	opacity: 1;
	animation: none !important;
}

/* A section previewed on its own, with no page around it. */
body.grade-section-preview { padding: 0; margin: 0; }
.grade-section-preview__empty {
	margin: 0; padding: 40px 24px; text-align: center;
	color: var(--grade-muted, #6b7280); font-style: italic;
}

/* ==================================================================
 * 0.16.0 - container decoration and the second wave of elements
 * ================================================================== */

/*
 * A full-width container is meant to reach the window edge. It sits inside the
 * theme's content wrapper, which has its own padding, so it has to break out.
 * overflow-x: clip on the wrapper - not hidden - stops the extra width causing
 * a scrollbar without turning the wrapper into a scroll container, which would
 * break every position: sticky inside it.
 */
.grade-main-content { overflow-x: clip; }
/*
 * Scoped to a top-level container in the page content on purpose. Header and
 * footer slots render their own .grade-content, and their containers are
 * already the full width of the slot - breaking those out shifts them sideways
 * by half the gutter instead.
 */
.grade-main-content > .grade-content > .grade-container--full {
	width: auto;
	/*
	 * Cancel the page's side padding exactly, rather than assuming the wrapper
	 * is centred in the viewport. calc(50% - 50vw) only reaches the edge when
	 * the padding matches on both sides, and Page Content Padding is a
	 * four-sided control - "30px 0 0 20px" left a 10px strip down the left.
	 * The fallback keeps the old behaviour on a theme that does not publish
	 * these two properties.
	 */
	margin-left: calc(-1 * var(--grade-content-padding-left, calc(50vw - 50%)));
	margin-right: calc(-1 * var(--grade-content-padding-right, calc(50vw - 50%)));
}
.grade-container--full > .grade-container__inner { max-width: none; }

/*
 * Designs that bring their own margins do not want ours as well.
 *
 * In :where(), like the rhythm rules it cancels, and for the same reason. At
 * its own weight this beat a margin set on the element itself, so switching a
 * container to "no gap" quietly disabled the Margin field on everything inside
 * it - and the cause was three levels up the tree from the control that had
 * stopped working. It still cancels the rhythm, because both weigh nothing and
 * this one is written later.
 */
:where(.grade-container--nogap .grade-column__inner > .grade-el + .grade-el) { margin-top: 0; }

.grade-container--sticky-top,
.grade-container--sticky-bottom { position: sticky; z-index: 20; }

.grade-container--decorated { position: relative; }

/* ---------- Container shape dividers ---------- */
.grade-container__shape {
	position: absolute;
	left: 0;
	width: 100%;
	line-height: 0;
	z-index: 0;
	pointer-events: none;
	overflow: hidden;
}
.grade-container__shape--top { top: 0; }
.grade-container__shape--bottom { bottom: 0; }
.grade-container__shape--front { z-index: 3; }
.grade-container__shape svg {
	display: block;
	width: 101%; /* a hair over, so subpixel rounding cannot show a seam */
	height: 80px;
	margin-left: -.5%;
	fill: #fff;
}
/* Content sits above a divider, which is drawn at z-index 0. Restated here
   next to the thing it is about; the position: relative this depends on is set
   with the rest of the inner's layout at the top of this file, so reading this
   line on its own makes the rule look inert when it is not. */
.grade-container > .grade-container__inner { z-index: 1; }

/* ---------- Container pattern overlay ---------- */
.grade-container--decorated::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background-image: var(--grade-pattern-image, none);
	background-size: var(--grade-pattern-size, 10px 10px);
	background-position: center top;
	background-attachment: var(--grade-pattern-attachment, scroll);
	mix-blend-mode: var(--grade-pattern-blend, normal);
	opacity: var(--grade-pattern-opacity, 0);
}

/* ---------- Section separator element ---------- */
.grade-el--section_separator { line-height: 0; }
.grade-shape__svg { display: block; width: 100%; height: 80px; fill: #fff; }
.grade-shape__svg--front { position: relative; z-index: 3; }

/* ---------- Decorative layer ---------- */
.grade-decor-host { position: static; }
/*
 * A decorative layer belongs to the band, not to the column it happens to be
 * dropped into - and a column is positioned, so without this the layer anchors
 * to the column box and lands somewhere near the middle of the text instead of
 * the middle of the band. The column holding one steps out of the way.
 */
.grade-column:has(.grade-decor-host) { position: static; }
.grade-decor {
	position: absolute;
	z-index: 0;
	pointer-events: none;
	max-width: none;
	opacity: var(--grade-decor-opacity, .09);
	will-change: transform;
}
.grade-decor--front { z-index: 4; }
.grade-decor img { display: block; width: 100%; height: auto; max-width: none; }
.grade-decor__text {
	display: block;
	white-space: nowrap;
	font-weight: 700;
	font-size: 12vw;
	line-height: 1;
	text-transform: uppercase;
}
.grade-decor--center       { top: 50%; left: 50%; transform: translate(-50%, -50%); }
.grade-decor--top-left     { top: 0; left: 0; }
.grade-decor--top-center   { top: 0; left: 50%; transform: translateX(-50%); }
.grade-decor--top-right    { top: 0; right: 0; }
.grade-decor--bottom-left  { bottom: 0; left: 0; }
.grade-decor--bottom-center{ bottom: 0; left: 50%; transform: translateX(-50%); }
.grade-decor--bottom-right { bottom: 0; right: 0; }

@media (prefers-reduced-motion: reduce) {
	.grade-decor { transform: var(--grade-decor-rest, translate(-50%, -50%)); }
	.grade-decor--top-left, .grade-decor--top-right,
	.grade-decor--bottom-left, .grade-decor--bottom-right { transform: none; }
}

/* ---------- Marquee ---------- */
.grade-marquee { overflow: hidden; position: relative; }
.grade-marquee--fade {
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.grade-marquee__track {
	display: flex;
	width: max-content;
	animation: gradeMarquee var(--grade-marquee-duration, 30s) linear infinite;
}
.grade-marquee--reverse .grade-marquee__track { animation-direction: reverse; }
.grade-marquee--pause:hover .grade-marquee__track,
.grade-marquee--pause:focus-within .grade-marquee__track { animation-play-state: paused; }
.grade-marquee__group {
	display: flex;
	align-items: center;
	gap: 48px;
	padding-right: 48px;
	flex: 0 0 auto;
}
.grade-marquee__item { display: inline-flex; align-items: center; gap: .5em; white-space: nowrap; text-decoration: none; }
.grade-marquee__sep { opacity: .5; }
@keyframes gradeMarquee { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
	.grade-marquee__track { animation: none; }
	.grade-marquee__group + .grade-marquee__group { display: none; }
}

/* ---------- Before / after ---------- */
.grade-ba {
	position: relative;
	overflow: hidden;
	user-select: none;
	touch-action: pan-y;
	--grade-ba-pos: 50%;
}
.grade-ba__pane { position: absolute; inset: 0; }
/*
 * The before pane is in flow so it gives the component its height when no
 * height control is set. It needs an explicit 100% for the case where one IS
 * set - otherwise it sizes to the natural image height, the container clips it,
 * and the label pinned to the pane's bottom edge ends up out of sight.
 */
.grade-ba__pane--before { position: relative; height: 100%; }
.grade-ba__pane--after { clip-path: inset(0 0 0 var(--grade-ba-pos)); }
.grade-ba--vertical .grade-ba__pane--after { clip-path: inset(var(--grade-ba-pos) 0 0 0); }
.grade-ba__img { display: block; width: 100%; height: 100%; object-fit: cover; }
.grade-ba__label {
	position: absolute;
	bottom: 12px;
	padding: .35em .75em;
	font-size: .8rem;
	background: rgba(0, 0, 0, .6);
	color: #fff;
	border-radius: 3px;
}
.grade-ba__label--before { left: 12px; }
.grade-ba__label--after { right: 12px; }
.grade-ba__line {
	position: absolute;
	top: 0;
	bottom: 0;
	left: var(--grade-ba-pos);
	width: 2px;
	margin-left: -1px;
	background: #fff;
	pointer-events: none;
}
.grade-ba--vertical .grade-ba__line {
	top: var(--grade-ba-pos);
	bottom: auto;
	left: 0;
	right: 0;
	width: auto;
	height: 2px;
	margin: -1px 0 0;
}
.grade-ba__grip {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 38px;
	height: 38px;
	margin: -19px 0 0 -19px;
	border-radius: 50%;
	background: #fff;
	box-shadow: 0 2px 10px rgba(0, 0, 0, .3);
}
.grade-ba--vertical .grade-ba__grip { top: 50%; left: 50%; }
/*
 * The range input is the actual control - transparent, stretched over the
 * whole image, so pointer and keyboard both drive the same element and the
 * component stays operable without a mouse.
 */
.grade-ba__range {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	margin: 0;
	opacity: 0;
	cursor: ew-resize;
	-webkit-appearance: none;
	appearance: none;
	background: none;
}
.grade-ba--vertical .grade-ba__range { cursor: ns-resize; }
.grade-ba__range:focus-visible ~ .grade-ba__line .grade-ba__grip { outline: 3px solid currentColor; outline-offset: 2px; }

/* ---------- Counter circle ---------- */
.grade-circle { position: relative; width: 180px; height: 180px; margin-inline: auto; }
.grade-circle__svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.grade-circle__track { stroke: rgba(0, 0, 0, .1); }
.grade-circle__value {
	stroke: var(--grade-color-primary, #2563eb);
	transition: stroke-dashoffset .1s linear;
}
.grade-circle__inner {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: .15em;
	text-align: center;
}
.grade-circle__number { font-size: 1.9rem; font-weight: 700; line-height: 1; }
.grade-circle__label { font-size: .8rem; opacity: .7; }

/* ---------- Social sharing ---------- */
.grade-share { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.grade-share__heading { display: block; margin-bottom: .5em; font-weight: 600; }
.grade-share__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: .5em;
	font-size: 18px;
	line-height: 1;
	text-decoration: none;
	border: 0;
	cursor: pointer;
	background: transparent;
	color: inherit;
	transition: transform .15s, opacity .15s;
}
.grade-share__link .dashicons { width: 1em; height: 1em; font-size: 1em; }
.grade-share__link:hover { transform: translateY(-2px); }
.grade-share--circle .grade-share__link,
.grade-share--square .grade-share__link { width: 2.3em; height: 2.3em; }
.grade-share--circle .grade-share__link { border-radius: 50%; }
.grade-share--square .grade-share__link { border-radius: 4px; }
.grade-share--pill .grade-share__link { padding: .6em 1.1em; border-radius: 999px; }
.grade-share--brand .grade-share__link { background: var(--grade-share-color, #333); color: #fff; }
.grade-share__label { font-size: .8em; }
.grade-share__link--copy.is-copied::after { content: attr(data-copied-label); font-size: .75em; }

/* ---------- Table of contents ---------- */
.grade-toc { display: block; }
.grade-toc__title {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: .5em;
	width: 100%;
	margin: 0 0 .6em;
	font-weight: 600;
	background: none;
	border: 0;
	padding: 0;
	color: inherit;
	text-align: left;
	cursor: inherit;
}
.grade-toc__title--toggle { cursor: pointer; }
.grade-toc__chevron { transition: transform .2s; }
.grade-toc.is-closed .grade-toc__chevron { transform: rotate(-90deg); }
.grade-toc.is-closed .grade-toc__list { display: none; }
.grade-toc__list { list-style: none; margin: 0; padding: 0; counter-reset: grade-toc; }
.grade-toc__item { margin: 0; }
.grade-toc__item--h3 { padding-left: 1.1em; }
.grade-toc__item--h4 { padding-left: 2.2em; }
.grade-toc__item--h5 { padding-left: 3.3em; }
.grade-toc__item--h6 { padding-left: 4.4em; }
.grade-toc__link {
	display: block;
	padding: .3em 0 .3em .8em;
	border-left: 2px solid transparent;
	text-decoration: none;
	color: inherit;
	transition: color .15s, border-color .15s;
}
.grade-toc--numbered .grade-toc__list { counter-reset: grade-toc; }
.grade-toc--numbered .grade-toc__link::before {
	counter-increment: grade-toc;
	content: counter(grade-toc) ". ";
	opacity: .6;
}
.grade-toc__link.is-active { border-color: currentColor; font-weight: 600; }
.grade-toc[hidden] { display: none; }

/* ---------- Modal ---------- */
.grade-modal-trigger {
	display: inline-flex;
	align-items: center;
	gap: .5em;
	cursor: pointer;
	border: 0;
	font: inherit;
	color: inherit;
}
.grade-modal-trigger--button { padding: .8em 1.6em; background: var(--grade-color-primary, #2563eb); color: #fff; }
.grade-modal-trigger--text { background: none; padding: 0; text-decoration: underline; }
.grade-modal {
	position: fixed;
	inset: 0;
	z-index: 10000;
	display: flex;
	padding: 24px;
	background: rgba(0, 0, 0, .6);
	overflow: auto;
	opacity: 0;
	transition: opacity .2s;
}
.grade-modal.is-open { opacity: 1; }
.grade-modal[hidden] { display: none; }
.grade-modal--center { align-items: center; justify-content: center; }
.grade-modal--top { align-items: flex-start; justify-content: center; }
.grade-modal--right { align-items: stretch; justify-content: flex-end; padding: 0; }
.grade-modal--left { align-items: stretch; justify-content: flex-start; padding: 0; }
.grade-modal--bottom { align-items: flex-end; justify-content: center; padding: 0; }
.grade-modal__dialog {
	position: relative;
	width: 640px;
	max-width: 100%;
	background: #fff;
	box-shadow: 0 24px 60px rgba(0, 0, 0, .3);
	transform: translateY(12px);
	transition: transform .2s;
}
.grade-modal.is-open .grade-modal__dialog { transform: none; }
.grade-modal--right .grade-modal__dialog,
.grade-modal--left .grade-modal__dialog { height: 100%; overflow: auto; }
.grade-modal__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1em;
	padding: 18px 20px;
	border-bottom: 1px solid rgba(0, 0, 0, .08);
}
.grade-modal__heading { margin: 0; font-weight: 600; }
.grade-modal__close {
	margin-left: auto;
	background: none;
	border: 0;
	cursor: pointer;
	color: inherit;
	line-height: 1;
	padding: 4px;
}
.grade-modal__body { padding: 24px; }
/*
 * In the canvas the dialog is laid out in the flow so its children can be
 * selected and styled. Pinning it to the viewport would put it out of reach of
 * the layer tree, which is the usual reason builder popups are painful to edit.
 */
.grade-modal--canvas {
	position: relative;
	inset: auto;
	opacity: 1;
	display: block;
	padding: 0;
	background: none;
	z-index: auto;
	outline: 1px dashed rgba(0, 0, 0, .25);
	outline-offset: 4px;
}
.grade-modal--canvas .grade-modal__dialog { transform: none; box-shadow: none; width: 100%; }

/* ---------- Carousel ---------- */
.grade-carousel { position: relative; --grade-carousel-per-view: 3; }
.grade-carousel__viewport { overflow: hidden; }
.grade-carousel__track {
	display: flex;
	gap: 24px;
	scroll-snap-type: x mandatory;
	overflow-x: auto;
	scrollbar-width: none;
	scroll-behavior: smooth;
	overscroll-behavior-x: contain;
}
.grade-carousel__track::-webkit-scrollbar { display: none; }
.grade-carousel--free .grade-carousel__track { scroll-snap-type: none; }
.grade-carousel__track > .grade-el {
	flex: 0 0 calc((100% - (var(--grade-carousel-per-view) - 1) * var(--grade-carousel-gap, 24px)) / var(--grade-carousel-per-view));
	margin-top: 0;
	scroll-snap-align: start;
}
.grade-carousel__arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 2;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border: 0;
	border-radius: 50%;
	background: rgba(255, 255, 255, .9);
	color: #111;
	cursor: pointer;
	box-shadow: 0 2px 10px rgba(0, 0, 0, .15);
}
.grade-carousel__arrow[disabled] { opacity: .35; cursor: default; }
.grade-carousel--arrows-inside .grade-carousel__arrow--prev { left: 10px; }
.grade-carousel--arrows-inside .grade-carousel__arrow--next { right: 10px; }
.grade-carousel--arrows-outside .grade-carousel__arrow--prev { left: -52px; }
.grade-carousel--arrows-outside .grade-carousel__arrow--next { right: -52px; }
.grade-carousel--arrows-top .grade-carousel__arrow,
.grade-carousel--arrows-bottom .grade-carousel__arrow { position: static; transform: none; }
.grade-carousel--arrows-top,
.grade-carousel--arrows-bottom { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.grade-carousel--arrows-top .grade-carousel__viewport,
.grade-carousel--arrows-bottom .grade-carousel__viewport { order: -1; flex: 1 0 100%; }
.grade-carousel--arrows-bottom .grade-carousel__viewport { order: -1; }
.grade-carousel__dots { display: flex; justify-content: center; gap: 8px; margin-top: 16px; }
.grade-carousel__dot {
	width: 9px;
	height: 9px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: rgba(0, 0, 0, .2);
	cursor: pointer;
}
.grade-carousel__dot.is-active { background: var(--grade-color-primary, #2563eb); }

/* ---------- Loop ---------- */
.grade-loop { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 32px; }
.grade-loop__item { min-width: 0; }
.grade-loop__item > .grade-el + .grade-el { margin-top: var(--grade-element-gap); }
.grade-loop--empty { display: block; opacity: .7; }
.grade-loop__pagination { display: flex; gap: .5em; justify-content: center; margin-top: 32px; }
.grade-loop__pagination .page-numbers { padding: .4em .8em; text-decoration: none; }
.grade-loop__pagination .current { font-weight: 700; }
.grade-loop__filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 32px; border-bottom: 1px solid rgba(0, 0, 0, .1); }
/* The tab colours are inherited and dimmed rather than named, so the row reads
   correctly on a dark section without anyone configuring it. */
.grade-loop__filter {
	appearance: none;
	margin: 0 0 -1px;
	padding: 12px 20px;
	border: 0;
	border-bottom: 2px solid transparent;
	background: transparent;
	font: inherit;
	font-weight: 600;
	font-size: .85em;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: inherit;
	/* Dimmed enough to read as unselected, not so far that it stops being
	   legible. At .6 the tabs sat under the contrast small text needs on a light
	   background, which made the one control on the page whose whole job is to
	   be read the hardest thing on it to read. */
	opacity: .78;
	cursor: pointer;
	transition: opacity .2s, color .2s, border-color .2s, background-color .2s;
}
.grade-loop__filter:hover { opacity: 1; }
.grade-loop__filter.is-active { opacity: 1; border-bottom-color: var(--grade-color-primary, #2563eb); }
.grade-loop__filter:focus-visible { outline: 2px solid var(--grade-color-primary, #2563eb); outline-offset: 2px; }
/* Filtering hides through a class so a site can swap this rule for a fade or a
   collapse without touching the script. */
.grade-loop__item.is-filtered-out { display: none; }

/* ---------- Search ---------- */
.grade-search { display: flex; align-items: stretch; gap: 8px; position: relative; }
.grade-search__input {
	flex: 1 1 auto;
	min-width: 0;
	padding: .7em .9em;
	border: 1px solid var(--grade-border, rgba(0, 0, 0, .15));
	background: #fff;
	font: inherit;
	color: inherit;
}
.grade-search__submit {
	padding: .7em 1.2em;
	border: 0;
	cursor: pointer;
	background: var(--grade-color-primary, #2563eb);
	color: #fff;
	font: inherit;
}
.grade-search--icon .grade-search__submit,
.grade-search--expand .grade-search__submit {
	position: absolute;
	right: 0;
	top: 0;
	bottom: 0;
	padding: 0 .9em;
	background: none;
	color: inherit;
}
.grade-search--icon .grade-search__input,
.grade-search--expand .grade-search__input { padding-right: 3em; }
.grade-search--expand .grade-search__input {
	width: 0;
	flex: 0 0 auto;
	padding-left: 0;
	padding-right: 2.6em;
	border-color: transparent;
	transition: width .25s, padding-left .25s;
}
.grade-search--expand.is-open .grade-search__input {
	width: 240px;
	padding-left: .9em;
	border-color: var(--grade-border, rgba(0, 0, 0, .15));
}

/* ---------- Widget area ---------- */
.grade-widget-area { display: flex; flex-direction: column; gap: 28px; }
.grade-widget-area ul { list-style: none; margin: 0; padding: 0; }

/* ---------- Lottie ---------- */
.grade-lottie { width: 100%; }
.grade-lottie svg { display: block; width: 100%; height: auto; }

/* ---------- Chart ---------- */
.grade-chart { position: relative; width: 100%; height: 300px; display: flex; }
.grade-chart svg { display: block; width: 100%; height: 100%; overflow: visible; }
.grade-chart__grid line { stroke: rgba(0, 0, 0, .08); stroke-width: 1; }
.grade-chart__label,
.grade-chart__value { font-size: 12px; fill: currentColor; }
.grade-chart__value { font-weight: 600; }
.grade-chart__bar,
.grade-chart__slice,
.grade-chart__line,
.grade-chart__area { transition: none; }

@media (max-width: 767px) {
	.grade-loop { grid-template-columns: minmax(0, 1fr); }
	.grade-carousel { --grade-carousel-per-view: 1; }
	.grade-carousel--arrows-outside .grade-carousel__arrow--prev { left: 6px; }
	.grade-carousel--arrows-outside .grade-carousel__arrow--next { right: 6px; }
}

/* ---------- Container height modes ---------- */
.grade-container--h-full > .grade-container__inner {
	min-height: 100vh;
	min-height: 100dvh; /* the real viewport on mobile, once the URL bar moves */
}
.grade-container--centered > .grade-container__inner { align-content: center; }
.grade-container--centered > .grade-container__inner > .grade-column { align-self: center; }

/* ---------- Background parallax ---------- */
.grade-container--parallax-fixed { background-attachment: fixed; }
.grade-container--parallax-up,
.grade-container--parallax-down { background-attachment: scroll; will-change: background-position; }

@media (max-width: 767px) {
	/*
	 * background-attachment: fixed is unreliable on iOS - it either judders or
	 * silently falls back to scroll, and either way the image is the wrong size.
	 * Opt back in per container with "Parallax on Mobile" if you have tested it.
	 */
	.grade-container--parallax-fixed:not([data-bg-parallax-mobile="1"]) { background-attachment: scroll; }
}

@media (prefers-reduced-motion: reduce) {
	.grade-container--parallax-fixed { background-attachment: scroll; }
}

/* ---------- Animation delay ---------- */
[data-grade-anim].grade-anim-in { animation-delay: var(--grade-anim-delay, 0ms); }

/* --------------------------------------------------------------------------
   Off Canvas

   The panel is fixed and the scrim covers the viewport, so neither can be
   pushed around by whatever the element happens to sit inside - a header that
   is itself position:sticky, most of the time.
   -------------------------------------------------------------------------- */

.grade-el--offcanvas { display: inline-flex; }

.grade-offcanvas__trigger {
	display: inline-flex;
	align-items: center;
	gap: .6em;
	background: none;
	border: 0;
	padding: 0;
	font: inherit;
	color: inherit;
	cursor: pointer;
}

.grade-offcanvas__bars {
	position: relative;
	width: 22px;
	height: 14px;
	display: inline-block;
	flex: none;
}

.grade-offcanvas__bars span,
.grade-offcanvas__bars::before,
.grade-offcanvas__bars::after {
	content: "";
	position: absolute;
	left: 0;
	width: 100%;
	height: 2px;
	background: currentColor;
	transition: transform .2s ease, opacity .2s ease;
}

.grade-offcanvas__bars::before { top: 0; }
.grade-offcanvas__bars span { top: 6px; }
.grade-offcanvas__bars::after { bottom: 0; }

[aria-expanded="true"] .grade-offcanvas__bars::before { transform: translateY(6px) rotate(45deg); }
[aria-expanded="true"] .grade-offcanvas__bars span { opacity: 0; }
[aria-expanded="true"] .grade-offcanvas__bars::after { transform: translateY(-6px) rotate(-45deg); }

/*
 * Cut bars. The ends are sheared and the three bars step to the right, so the
 * mark reads as part of an angular identity rather than a generic hamburger.
 * The stagger comes off when it becomes a cross, or the arms would not meet.
 */
.grade-offcanvas__trigger--cut .grade-offcanvas__bars span,
.grade-offcanvas__trigger--cut .grade-offcanvas__bars::before,
.grade-offcanvas__trigger--cut .grade-offcanvas__bars::after {
	clip-path: polygon(2px 0, 100% 0, calc(100% - 2px) 100%, 0 100%);
	transition: transform .3s ease, opacity .25s ease, clip-path .3s ease;
}

.grade-offcanvas__trigger--cut .grade-offcanvas__bars span { transform: translateX(30%); }
.grade-offcanvas__trigger--cut .grade-offcanvas__bars::after { transform: translateX(60%); }

.grade-offcanvas__trigger--cut[aria-expanded="true"] .grade-offcanvas__bars span,
.grade-offcanvas__trigger--cut[aria-expanded="true"] .grade-offcanvas__bars::before,
.grade-offcanvas__trigger--cut[aria-expanded="true"] .grade-offcanvas__bars::after {
	clip-path: none;
}

.grade-offcanvas__trigger--cut[aria-expanded="true"] .grade-offcanvas__bars::after { transform: translateY(-6px) rotate(-45deg); }

.grade-offcanvas {
	position: fixed;
	inset: 0;
	z-index: 9998;
	--grade-offcanvas-speed: 280ms;
}

.grade-offcanvas[hidden] { display: none; }

.grade-offcanvas__scrim {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, .5);
	opacity: 0;
	transition: opacity var(--grade-offcanvas-speed) ease;
}

.grade-offcanvas.is-open .grade-offcanvas__scrim { opacity: 1; }

.grade-offcanvas__panel {
	position: absolute;
	background: #fff;
	/*
	 * The panel covers the screen edge, but in the markup it still sits wherever
	 * the trigger does - often a right-aligned column in a header. Inheriting
	 * that alignment pushed every item in the menu to the right. The panel is
	 * its own place and sets its own.
	 */
	text-align: start;
	max-width: 100%;
	max-height: 100%;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	padding: 2rem;
	transition: transform var(--grade-offcanvas-speed) ease;
	will-change: transform;
}

.grade-offcanvas__panel:focus { outline: none; }

.grade-offcanvas--left  .grade-offcanvas__panel { top: 0; bottom: 0; left: 0; width: 380px; transform: translateX(-100%); }
.grade-offcanvas--right .grade-offcanvas__panel { top: 0; bottom: 0; right: 0; width: 380px; transform: translateX(100%); }
.grade-offcanvas--top    .grade-offcanvas__panel { top: 0; left: 0; right: 0; height: 60vh; transform: translateY(-100%); }
.grade-offcanvas--bottom .grade-offcanvas__panel { bottom: 0; left: 0; right: 0; height: 60vh; transform: translateY(100%); }

/*
 * Full screen. Not a drawer that happens to be wide - the panel covers the
 * viewport and fades in with a short drop, because sliding a full-screen sheet
 * across the screen reads as a page change rather than a menu opening.
 */
.grade-offcanvas--full .grade-offcanvas__panel {
	inset: 0;
	width: 100%;
	height: 100%;
	max-width: none;
	max-height: none;
	transform: translateY(-10px);
	opacity: 0;
	visibility: hidden;
	transition: opacity var(--grade-offcanvas-speed) ease,
	            transform var(--grade-offcanvas-speed) ease,
	            visibility var(--grade-offcanvas-speed) ease;
}

.grade-offcanvas--full.is-open .grade-offcanvas__panel {
	opacity: 1;
	visibility: visible;
}

.grade-offcanvas--full .grade-offcanvas__scrim { display: none; }

.grade-offcanvas.is-open .grade-offcanvas__panel { transform: none; }

.grade-offcanvas__close {
	position: absolute;
	top: .75rem;
	right: .75rem;
	background: none;
	border: 0;
	font-size: 28px;
	line-height: 1;
	cursor: pointer;
	color: inherit;
	padding: .25rem .5rem;
}

/* position:fixed loses the scroll position, so the panel's script stores it
   and puts it back. Without the fixed body, iOS scrolls the page behind. */
body.grade-offcanvas-open {
	position: fixed;
	width: 100%;
	overflow: hidden;
	/* The gutter the scrollbar was occupying, measured when the panel opened.
	   Without it the page widens the moment the menu appears and everything on
	   it - the logo most visibly - slides sideways and back again. */
	padding-right: var(--grade-scrollbar-width, 0px);
}

/* The header is positioned against the viewport rather than the body, so the
   body's padding does not reach it and it has to be inset by the same amount
   or it alone moves while the page under it stays still. */
body.grade-offcanvas-open .grade-slot--header,
body.grade-offcanvas-open .grade-header {
	right: var(--grade-scrollbar-width, 0px);
}

/* In the canvas the drawer is laid out inline, so its contents can be selected
   and styled. Hiding it would make everything inside unreachable. */
.grade-offcanvas--canvas {
	position: relative;
	inset: auto;
}

.grade-offcanvas--canvas .grade-offcanvas__scrim { display: none; }

.grade-offcanvas--canvas .grade-offcanvas__panel {
	position: relative;
	inset: auto;
	transform: none;
	width: auto;
	height: auto;
	border: 1px dashed rgba(0, 0, 0, .25);
}

@media (prefers-reduced-motion: reduce) {
	.grade-offcanvas__panel,
	.grade-offcanvas__scrim { transition: none; }
}

/* ------------------------------------------------------------------ */
/* Toggle                                                              */
/* ------------------------------------------------------------------ */
/*
 * The defaults are fallbacks inside var(), not declarations on .grade-toggle.
 * Declared here they would be set ON the element, and a value inherited from
 * the wrapper - which is where the generated CSS puts it - loses to a local
 * declaration every time. That is why the divider setting drew nothing.
 */
.grade-toggle {
	--grade-toggle-speed: 260ms;
	border-top: var(--grade-toggle-divider, 0px) solid var(--grade-toggle-divider-color, currentColor);
}

/*
 * The closing line is a class, not :last-child. Every toggle sits in its own
 * element wrapper, so :last-child is true for all of them - it drew a line
 * under every row, doubling up with the next row's line above.
 */
.grade-toggle--last-divider { border-bottom: var(--grade-toggle-divider, 0px) solid var(--grade-toggle-divider-color, currentColor); }

.grade-toggle__row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin: 0;
	gap: 12px;
}

.grade-toggle__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	flex: 1 1 auto;
	width: 100%;
	padding: 14px 0;
	background: none;
	border: 0;
	font: inherit;
	color: inherit;
	text-align: left;
	text-decoration: none;
	cursor: pointer;
}

.grade-toggle__head--link { flex: 0 1 auto; }
.grade-toggle__head--static { cursor: default; }

.grade-toggle__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 14px 0;
	background: none;
	border: 0;
	color: inherit;
	cursor: pointer;
}

.grade-toggle--icon-start .grade-toggle__head { flex-direction: row-reverse; justify-content: flex-end; }
.grade-toggle--icon-start .grade-toggle__row { flex-direction: row-reverse; justify-content: flex-end; }

.grade-toggle__label { display: block; }

/* The indicator is drawn, not fetched. Two rules instead of an icon font. */
.grade-toggle__icon {
	position: relative;
	flex: 0 0 auto;
	width: 20px;
	height: 20px;
}

.grade-toggle__icon--plus::before,
.grade-toggle__icon--plus::after {
	content: "";
	position: absolute;
	inset: 50% 0 auto 0;
	height: 1.5px;
	background: currentColor;
	transform: translateY(-50%);
	transition: transform var(--grade-toggle-speed) ease, opacity var(--grade-toggle-speed) ease;
}

.grade-toggle__icon--plus::after { transform: translateY(-50%) rotate(90deg); }
.grade-toggle.is-open .grade-toggle__icon--plus::after { transform: translateY(-50%) rotate(0deg); opacity: 0; }

.grade-toggle__icon--chevron::before {
	content: "";
	position: absolute;
	inset: 25% 25% auto 25%;
	height: 50%;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate(45deg);
	transform-origin: center;
	transition: transform var(--grade-toggle-speed) ease;
}

.grade-toggle.is-open .grade-toggle__icon--chevron::before { transform: rotate(-135deg); }

.grade-toggle__panel {
	height: 0;
	overflow: hidden;
	transition: height var(--grade-toggle-speed) ease;
}

.grade-toggle.is-open .grade-toggle__panel { height: auto; }
.grade-toggle__panel[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
	.grade-toggle__panel,
	.grade-toggle__icon--plus::before,
	.grade-toggle__icon--plus::after,
	.grade-toggle__icon--chevron::before { transition: none; }
}

/* ------------------------------------------------------------------ */
/* Button shapes                                                       */
/* ------------------------------------------------------------------ */
/*
 * Cut out of the button rather than drawn behind it, so the shape survives
 * whatever is underneath - an image, a gradient, another coloured band. A
 * clipped element cannot show a border or a shadow, which is why these are
 * offered as a choice rather than layered on top of the existing styles.
 */
.grade-button[class*="grade-button--shape-"] {
	--grade-button-cut: 24px;
	border-radius: 0;
	border-color: transparent;
}

/*
 * The label is inset rather than the button padded. Padding is a setting the
 * designer owns, and the generated stylesheet writes it as a shorthand that
 * would win over anything set here - so a shaped button with tight padding had
 * its text sliced off by its own shape. The label's margin is nothing else's
 * business, so insetting it works whatever the padding is.
 */
.grade-button--shape-skew-right {
	clip-path: polygon(0 0, calc(100% - var(--grade-button-cut)) 0, 100% 100%, var(--grade-button-cut) 100%);
}

.grade-button--shape-skew-left {
	clip-path: polygon(var(--grade-button-cut) 0, 100% 0, calc(100% - var(--grade-button-cut)) 100%, 0 100%);
}

.grade-button--shape-skew-right > .grade-button__label,
.grade-button--shape-skew-left > .grade-button__label {
	margin-inline: var(--grade-button-cut);
}

.grade-button--shape-cut-tr {
	clip-path: polygon(0 0, calc(100% - var(--grade-button-cut)) 0, 100% var(--grade-button-cut), 100% 100%, 0 100%);
}

/* The left-hand pair. Only the two right corners existed, which is an odd set
   to offer: a page with a left-aligned button and a right-aligned one cannot
   have them cut the same way as each other. */
.grade-button--shape-cut-tl {
	clip-path: polygon(var(--grade-button-cut) 0, 100% 0, 100% 100%, 0 100%, 0 var(--grade-button-cut));
}

.grade-button--shape-cut-bl {
	clip-path: polygon(0 0, 100% 0, 100% 100%, var(--grade-button-cut) 100%, 0 calc(100% - var(--grade-button-cut)));
}

.grade-button--shape-cut-br {
	clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--grade-button-cut)), calc(100% - var(--grade-button-cut)) 100%, 0 100%);
}

.grade-button--shape-chevron {
	clip-path: polygon(0 0, calc(100% - var(--grade-button-cut)) 0, 100% 50%, calc(100% - var(--grade-button-cut)) 100%, 0 100%);
}

.grade-button--shape-chevron > .grade-button__label { margin-inline-end: var(--grade-button-cut); }
.grade-button--shape-cut-tr > .grade-button__label { margin-inline-end: calc(var(--grade-button-cut) / 2); }
.grade-button--shape-cut-br > .grade-button__label { margin-inline-end: calc(var(--grade-button-cut) / 2); }

/* ------------------------------------------------------------------ */
/* Scramble                                                            */
/* ------------------------------------------------------------------ */
/* The line is set in a monospaced-ish way while it resolves, so characters
   swapping does not reflow the words around them. */
.grade-scramble {
	margin: 0;
	font-variant-ligatures: none;
	font-kerning: none;
	overflow-wrap: anywhere;
}

/* The unsettled characters take the accent, so the noise reads as noise
   rather than as part of the sentence. The fallback is here rather than only
   in the generated CSS so that an element saved before the colour control
   existed still comes out looking right. */
.grade-scramble__dud { color: var(--grade-scramble-dud, #ef4136); }

/* ------------------------------------------------------------------ */
/* Word Wheel                                                          */
/* ------------------------------------------------------------------ */
.grade-wheel {
	--grade-wheel-focus: #ffffff;
	--grade-wheel-dim: .14;
	position: relative;
	height: 455px;
	overflow: hidden;
	/* The list fades out at the top and bottom rather than being cut off. */
	-webkit-mask-image: linear-gradient(rgba(0,0,0,0) 0, #000 16%, #000 84%, rgba(0,0,0,0) 100%);
	mask-image: linear-gradient(rgba(0,0,0,0) 0, #000 16%, #000 84%, rgba(0,0,0,0) 100%);
}

.grade-wheel__track {
	list-style: none;
	margin: 0;
	padding: 0;
	will-change: transform;
}

.grade-wheel__item {
	--grade-wheel-away: 1;
	color: var(--grade-wheel-focus);
	/* Full colour in the middle, down to the faded value at the edges. */
	opacity: calc(1 - (1 - var(--grade-wheel-dim)) * var(--grade-wheel-away));
	line-height: 1.35;
}

/* No script, or no appetite for movement: a plain readable list. */
.grade-wheel.is-static {
	height: auto;
	-webkit-mask-image: none;
	mask-image: none;
}

.grade-wheel.is-static .grade-wheel__item { opacity: 1; }
.grade-wheel.is-static .grade-wheel__item[aria-hidden="true"] { display: none; }

/*
 * A container that is one link.
 *
 * The anchor is a transparent layer over the whole box rather than a wrapper
 * around it, because a card can hold an image that carries its own link and an
 * anchor inside an anchor is invalid - the browser closes the outer one early
 * and the bottom half of the card stops working. Everything interactive inside
 * lifts above the layer and keeps its own destination; everything else is
 * covered by it, so the blank space between the picture and the title is part
 * of the same click.
 */
.grade-container--linked { position: relative; }

.grade-container--linked > .grade-container__link {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: block;
	border-radius: inherit;
	/* No text, so nothing to underline, and nothing to inherit a colour from. */
	font-size: 0;
	text-decoration: none;
}

/*
 * The inner wrapper must not create a stacking context here.
 *
 * .grade-container__inner carries z-index: 1 as standard. The link layer also
 * sat at z-index 1, and when two positioned elements share a z-index the later
 * one in the document wins - so the content painted over the link and swallowed
 * every click. The card looked linked, reported the right href, and did
 * nothing.
 *
 * Setting the inner back to auto lets the link (z-index 1) sit above it, while
 * leaving the inner without a stacking context of its own - which is what
 * allows a genuinely interactive child below to lift back above the link.
 */
.grade-container--linked > .grade-container__inner {
	position: relative;
	z-index: auto;
}

.grade-container--linked a:not(.grade-container__link),
.grade-container--linked button,
.grade-container--linked input,
.grade-container--linked select,
.grade-container--linked textarea,
.grade-container--linked [tabindex]:not([tabindex="-1"]) {
	position: relative;
	z-index: 2;
}

/* Keyboard focus has to be visible on a link that draws nothing. */
.grade-container--linked > .grade-container__link:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}


/*
 * Sticky header on a phone.
 *
 * WordPress uses two breakpoints for its admin bar, not one: at 782px it
 * grows from 32px to 46px, and at 600px it stops being fixed and scrolls
 * away with the page. Only the first was honoured, so a signed-in visitor
 * on a phone got a header pinned 46px down the screen with a strip of page
 * showing above it. Nobody signed out ever saw it.
 */
.grade-slot--header.is-transparent.is-sticky { position: fixed; }

@media (max-width: 1024px) {
	.grade-slot--header.is-transparent.is-sticky:not(.is-sticky-tablet) { position: absolute; }
}

@media (max-width: 767px) {
	.grade-slot--header.is-transparent.is-sticky:not(.is-sticky-mobile) { position: absolute; }
}

@media (max-width: 782px) {
	body.admin-bar .grade-slot--header.is-sticky,
	body.admin-bar .grade-slot--header.is-transparent { top: 46px; }
}

@media (max-width: 600px) {
	body.admin-bar .grade-slot--header.is-sticky,
	body.admin-bar .grade-slot--header.is-transparent { top: 0; }
}
