/**
 * NYSF design tokens.
 *
 * Ported verbatim from the prototype's nysf-component-library/brand.css :root block.
 * brand.css remains the source of truth: if a value changes there, change it here.
 *
 * Elementor's Global Styles kit holds the same palette and typography so the
 * editor panel shows the right swatches. These custom properties are what the
 * widget stylesheets and template overrides actually consume.
 */

:root {
	/* ---- Palette ---- */
	--nysf-navy: #092938;      /* Deep Space Navy, ~60% of the page */
	--nysf-twilight: #2e5a70;  /* Twilight Blue, support */
	--nysf-gold: #efaf3a;      /* Solar Gold, accent, celebrates */
	--nysf-gold-d: #e0a126;    /* Solar Gold, darker state */
	--nysf-flare: #c73e5e;     /* Solar Flare, accent, humanises */

	/*
	 * Solar Flare, darkened 7%, for anything that is small text.
	 *
	 * The brand swatch is #c73e5e, and against First Light it measures
	 * 4.45:1. WCAG 2.2 AA wants 4.5:1 for text below 18.66px bold, so every
	 * eyebrow, category label and testimonial card on the site missed by
	 * 0.05. This is the smallest change that clears it, with headroom: 4.81
	 * on cream, 5.21 on card, and 4.81 the other way round for First Light
	 * text sitting on a flare panel.
	 *
	 * Kept as a second token rather than moving the brand colour, so the
	 * swatch NYSF and Good Apples work from stays exactly what the brand
	 * book says. Use --nysf-flare for anything decorative or 18.66px bold
	 * and up, --nysf-flare-ink for small text and for a panel that carries
	 * small text.
	 */
	--nysf-flare-ink: #be3b5a;
	--nysf-light: #fbf3e4;     /* First Light, ~30% warm white */

	/* ---- Roles ---- */
	--nysf-bg: var(--nysf-light);
	--nysf-card: #fffdf8;
	--nysf-ink: var(--nysf-navy);
	--nysf-ink-soft: #4a5f6a;
	--nysf-line: #ece0cb;
	--nysf-line-navy: #1d4254;

	/* ---- Shape and depth ---- */
	--nysf-r-lg: 24px;   /* everyday default, rounded rectangle */
	--nysf-r-md: 18px;
	--nysf-r-pill: 40px; /* labels and secondary actions */
	--nysf-arch: 160px 160px 22px 22px; /* headshots and focal images, echoes the logo dome */
	--nysf-shadow: 0 24px 60px -28px rgba(9, 41, 56, 0.3);
	--nysf-shadow-sm: 0 14px 34px -22px rgba(9, 41, 56, 0.32);

	/* ---- Type ---- */
	--nysf-display: 'Bricolage Grotesque', system-ui, sans-serif;
	--nysf-sans: 'Figtree', system-ui, sans-serif;
	--nysf-quote: 'Fraunces', Georgia, serif;

	/* ---- Layout ---- */
	--nysf-maxw: 1240px;
}

/**
 * Self-hosted fonts.
 *
 * All three families are SIL Open Font Licence, so self-hosting is permitted.
 * Serving them ourselves rather than from the Google CDN (as the prototype did)
 * means one less third-party connection, no third-party request logging, and
 * no render-blocking DNS lookup on first paint.
 *
 * These are variable fonts: one file covers the whole weight range, which is
 * why each rule declares a range rather than a single font-weight. The browser
 * interpolates. Do not add per-weight @font-face rules.
 */

@font-face {
	font-family: 'Bricolage Grotesque';
	src: url('../fonts/BricolageGrotesque-Variable.woff2') format('woff2');
	font-weight: 200 800;
	font-stretch: 75% 100%;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: 'Figtree';
	src: url('../fonts/Figtree-Variable.woff2') format('woff2');
	font-weight: 300 900;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: 'Fraunces';
	src: url('../fonts/Fraunces-Variable.woff2') format('woff2');
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
}

/*
 * Fraunces italic is a separate file from Google, and it is the one the design
 * actually uses: brand.css sets --quote to Fraunces and the prototype applies
 * it to pull quotes only, in italic.
 *
 * Without this, pull quotes render upright, or the browser fakes a slant,
 * which on a display serif looks visibly wrong.
 *
 * Loaded under the same family name with font-style: italic, so the browser
 * picks the right file automatically wherever --nysf-quote is used.
 */

@font-face {
	font-family: 'Fraunces';
	src: url('../fonts/Fraunces-Italic-Variable.woff2') format('woff2');
	font-weight: 100 900;
	font-style: italic;
	font-display: swap;
}

/* ---- Base typography, matching the prototype ---- */

body {
	font-family: var(--nysf-sans);
	background: var(--nysf-bg);
	color: var(--nysf-ink);
	font-size: 16px;
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4,
h5,
h6 {
	font-family: var(--nysf-display);
	font-weight: 700;
	letter-spacing: -0.5px;
	line-height: 1.08;
}

/*
 * A heading scale, because the parent theme's is enormous.
 *
 * Inlingua's style.css sets `h2 { font-size: 76px }` and `h3 { 62px }`.
 * tokens.css previously only capped headings inside .nysf-head-row, so every
 * heading outside one (the split sections, the testimonial and steps heads,
 * the people grids) rendered at 76px against the design's 42 to 46. It read
 * as a different page rather than as a slightly big heading.
 *
 * brand.css has no base scale of its own: it sizes each heading in context.
 * These are those contextual values collected into defaults, so a heading is
 * right without a component rule and a component can still override.
 */
h1 {
	font-size: clamp( 40px, 5vw, 64px );
	line-height: 1.04;
}

h2,
.elementor-widget-heading h2.elementor-heading-title {
	font-size: clamp( 32px, 3.6vw, 46px );
	line-height: 1.06;
}

h3,
.elementor-widget-heading h3.elementor-heading-title {
	font-size: 22px;
	line-height: 1.2;
}

h4,
.elementor-widget-heading h4.elementor-heading-title {
	font-size: 20px;
	line-height: 1.2;
}

h5,
h6 {
	font-size: 17px;
	line-height: 1.25;
}

blockquote,
.nysf-pullquote {
	font-family: var(--nysf-quote);
	font-style: italic;
}

/* ---- Shared shape helpers ---- */

.nysf-arch {
	border-radius: var(--nysf-arch);
	overflow: hidden;
}

.nysf-panel-dark {
	background: var(--nysf-navy);
	color: var(--nysf-light);
	border-radius: var(--nysf-r-lg);
}

.nysf-panel-dark :is(h1, h2, h3, h4, h5, h6) {
	color: var(--nysf-light);
}

/*
 * Editor-only placeholder shown when a widget has nothing to render because
 * NYSF Core is inactive. Never appears on the front end.
 */
.nysf-widget-notice {
	padding: 18px 20px;
	border: 1px dashed var(--nysf-flare);
	border-radius: var(--nysf-r-md);
	background: var(--nysf-card);
	color: var(--nysf-ink-soft);
	font-size: 14px;
}

/* ---- Typography overrides against the parent theme ---- */

/*
 * Inlingua sets --e-global-typography-secondary-text-transform: capitalize and
 * applies it to headings, which title-cased every heading on the site:
 * "Thinking About A Future In STEM?" instead of the sentence case the design
 * uses. apply-kit now writes text_transform: none into the Elementor kit; this
 * catches anything the kit does not reach, including the theme's own markup.
 */
:root {
	--e-global-typography-primary-text-transform: none;
	--e-global-typography-secondary-text-transform: none;
	--e-global-typography-text-text-transform: none;
	--e-global-typography-accent-text-transform: none;
}

h1,
h2,
h3,
h4,
h5,
h6,
.elementor-heading-title {
	text-transform: none;
}

/* ---- Section furniture ---- */

/*
 * Sections carry their 72px on the bottom only (see section_padding() in
 * class-page-builder.php), which gives 72px between any two of them. The one
 * that also needs it on top is the first section under the hero, and only the
 * DOM knows which that is.
 *
 * Elementor emits container padding as `.e-con { padding-block-start:
 * var(--padding-block-start) }`, one class, so this two-class selector wins
 * without !important.
 */
.nysf-hero + .e-con {
	padding-block-start: 72px;
}

/* brand.css .eyebrow */
.nysf-eyebrow {
	font-size: 13px;
	font-weight: 700;
	letter-spacing: 2px;
	text-transform: uppercase;
	/* 13px bold is small text: 4.5:1, not 3:1. See --nysf-flare-ink. */
	color: var(--nysf-flare-ink);
	margin-bottom: 14px;
}

/*
 * On the navy hero, Solar Flare on near-black fails contrast badly. Gold is
 * the accent that works on dark. This is a class rather than an inline colour
 * because .nysf-eyebrow sets colour on the element itself, which outranks
 * Elementor's wrapper-level text_color.
 */
.nysf-eyebrow--light {
	color: var(--nysf-gold);
}

/*
 * Centred section head, used by the numbered-steps sections. The eyebrow is
 * an inline-flex element, so text-align alone does not move it.
 */
.nysf-centred {
	text-align: center;
}

.nysf-centred > .e-con-inner > * {
	margin-inline: auto;
}

.nysf-centred .nysf-eyebrow {
	justify-content: center;
}

.nysf-centred .elementor-heading-title,
.nysf-centred .nysf-lead {
	margin-inline: auto;
}

.nysf-centred .nysf-btn-row,
.nysf-centred .nysf-btn-row > .e-con-inner {
	justify-content: center;
}

/* ==========================================================================
   Program detail

   brand.css .cdetail: copy column and sticky fact box. The 1.7fr / 0.9fr
   grid ratio is set as 65% / 35% on the two column containers by the page
   builder, through Elementor's Width control rather than a `flex` rule
   here — see the note in section_detail(). The row is nowrap, so the two
   columns shrink to absorb the 38px gap.

   The fact box is position: sticky, so the aside must be allowed to stretch
   to the full row height. Nothing here sets align-self.
   ========================================================================== */

@media (max-width: 900px) {
	/* Custom properties inherit, so setting --flex-wrap on the section
	   reaches the .e-con-inner that actually carries flex-wrap. Nothing
	   sets --flex-wrap per element, so a plain class wins here. */
	.nysf-detail {
		--flex-wrap: wrap;
	}

	/* The 65/35 widths are set per element, at
	   `.elementor-N .elementor-element.elementor-element-xxx` — three
	   classes. Matching on .e-con as well takes this to four so it wins. */
	.nysf-detail.e-con > .e-con,
	.nysf-detail.e-con > .e-con-inner > .e-con {
		--width: 100%;
	}
}

/* brand.css .cblock: 34px between blocks, and a smaller heading than a
   section head, because these are subheads inside one section. */
.nysf-detail__main > .elementor-widget-heading {
	margin-top: 34px;
}

.nysf-detail__main > .elementor-widget-heading:first-child {
	margin-top: 0;
}

/* The global scale earlier in this file is
   `.elementor-widget-heading h2.elementor-heading-title`, two classes and an
   element. A plain `.nysf-detail__main .elementor-heading-title` is two
   classes and loses to it, which is why these subheads were rendering at the
   section-head 46px. Repeat the widget and tag parts to win. */
.nysf-detail__main .elementor-widget-heading .elementor-heading-title,
.nysf-detail__main .elementor-widget-heading h2.elementor-heading-title,
.nysf-detail__main .elementor-widget-heading h3.elementor-heading-title {
	font-size: 26px;
	line-height: 1.15;
	margin-bottom: 16px;
}

.nysf-detail__main p {
	color: var(--nysf-ink-soft);
}

/* brand.css uses .learn-grid to run the checklist in two columns. */
.nysf-checklist--two {
	grid-template-columns: repeat(2, minmax(0, 1fr));
	column-gap: 24px;
}

@media (max-width: 620px) {
	.nysf-checklist--two {
		grid-template-columns: 1fr;
	}
}

/*
 * The policies page: the same ticked list, but every item is a link to a
 * PDF, and the design centres it in a 1000px column with a wider row gap.
 */
.nysf-checklist--docs {
	max-width: 1000px;
	margin-inline: auto;
	gap: 18px;
}

.nysf-checklist--docs a {
	color: var(--nysf-navy);
	text-decoration: underline;
	text-decoration-color: var(--nysf-line);
	text-underline-offset: 3px;
}

.nysf-checklist--docs a:hover,
.nysf-checklist--docs a:focus-visible {
	text-decoration-color: currentColor;
	color: var(--nysf-twilight);
}

/* The pull quote in a detail block: gold rule, Fraunces italic. */
.nysf-detail__main .nysf-pullquote {
	font-family: var(--nysf-quote);
	font-style: italic;
	font-size: 24px;
	line-height: 1.4;
	color: var(--nysf-navy);
	border-left: 4px solid var(--nysf-gold);
	padding: 6px 0 6px 24px;
	margin: 0;
}

.nysf-detail__main .nysf-pullquote cite {
	display: block;
	margin-top: 10px;
	font-family: var(--nysf-sans);
	font-style: normal;
	font-size: 14px;
	color: var(--nysf-ink-soft);
}

/* ---- Checklists ---- */

.nysf-checklist {
	list-style: none;
	margin: 18px 0 24px;
	padding: 0;
	display: grid;
	gap: 10px;
}

.nysf-checklist li {
	position: relative;
	padding-left: 30px;
}

.nysf-checklist li::before {
	content: '';
	position: absolute;
	left: 0;
	top: 0.42em;
	width: 18px;
	height: 18px;
	border-radius: 50%;
	background: var(--nysf-gold);
}

.nysf-checklist li::after {
	content: '';
	position: absolute;
	left: 6px;
	top: 0.62em;
	width: 5px;
	height: 9px;
	border: solid var(--nysf-navy);
	border-width: 0 2px 2px 0;
	transform: rotate(45deg);
}

.nysf-lead {
	font-size: 18px;
	line-height: 1.55;
	max-width: 62ch;
}

/* Pull quotes and placeholder blockquotes */
blockquote cite {
	display: block;
	margin-top: 10px;
	font-family: var(--nysf-sans);
	font-style: normal;
	font-size: 14px;
	color: var(--nysf-ink-soft);
}

/*
 * Inlingua sets blockquote { padding: 90px } in style.css, which is what left
 * those tall empty gaps around each quote: the element was ~200px high for two
 * lines of text. Reset it and use a gold rule instead.
 */
.elementor-widget-text-editor blockquote {
	margin: 0 0 20px;
	padding: 0 0 0 18px;
	border-left: 3px solid var(--nysf-gold);
	font-size: 18px;
	line-height: 1.5;
}

.elementor-widget-text-editor blockquote p {
	font-size: inherit;
	line-height: inherit;
	margin-bottom: 0;
}

/* ==========================================================================
   Brand components
   These carry the prototype's shape language, which the theme has no concept
   of. Styling lives here rather than in per-instance Elementor controls so a
   brand change is one edit, not a rebuild of every page.
   ========================================================================== */

/* ---- Eyebrows: the design prefixes them with an em rule ---- */

/*
 * The rule is gold, not the eyebrow's own colour. brand.css sets
 * `.eyebrow::before { background: var(--gold) }` against flare text, so
 * currentColor rendered it red and lost the two-tone the design intends.
 * Gold holds on the navy panels too, where the text switches to gold anyway.
 */
.nysf-eyebrow::before {
	content: '';
	display: inline-block;
	width: 22px;
	height: 2px;
	margin-right: 9px;
	vertical-align: middle;
	background: var(--nysf-gold);
}

/* ---- Buttons: pill, sentence case, circular arrow ---- */

/*
 * Two shapes of button share these styles.
 *
 * Elementor's button widget renders <a class="elementor-button"> inside a
 * wrapper that carries our class, so the rules have to reach a descendant.
 * Our own templates (the event row, and anything else rendered in PHP) put
 * the class straight on the anchor. Written only as a descendant selector,
 * every template-rendered button silently lost its pill, background and
 * arrow and rendered as plain link text.
 *
 * :is() keeps one declaration block for both rather than fourteen duplicated
 * rules, and takes the higher of the two specificities, which is what the
 * Elementor case already needed.
 */

:is(.nysf-btn .elementor-button, a.nysf-btn, button.nysf-btn, input.nysf-btn) {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	font-family: var(--nysf-sans);
	font-weight: 600;
	font-size: 15px;
	/*
	 * 26px, the height of the arrow disc, so a button is the same height
	 * whether or not it carries one. With `line-height: 1` the ghost half of
	 * a pair sat 11px shorter than its gold partner, because the gold one's
	 * height came from the arrow and the ghost's from a 15px line box.
	 */
	line-height: 26px;
	padding: 15px 26px;
	border-radius: var(--nysf-r-pill) !important;
	/*
	 * No border, on any variant. The outlined variants draw their outline
	 * with an inset box-shadow instead, which takes no layout space, so a
	 * ghost button is exactly the same size as the gold one beside it at
	 * every size without a padding adjustment per variant.
	 */
	border: 0;
	text-transform: none;
	transition: 0.25s;
}

:is(.nysf-btn .elementor-button, a.nysf-btn, button.nysf-btn, input.nysf-btn)::after {
	content: '→';
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	border-radius: 50%;
	font-size: 13px;
	transition: 0.25s;
}

:is(.nysf-btn--gold .elementor-button, a.nysf-btn--gold, button.nysf-btn--gold, input.nysf-btn--gold) {
	background: var(--nysf-gold);
	color: var(--nysf-navy);
}

:is(.nysf-btn--gold .elementor-button, a.nysf-btn--gold, button.nysf-btn--gold, input.nysf-btn--gold)::after {
	background: var(--nysf-navy);
	color: var(--nysf-gold);
}

:is(.nysf-btn--gold .elementor-button, a.nysf-btn--gold, button.nysf-btn--gold, input.nysf-btn--gold):hover {
	background: var(--nysf-gold-d);
}

:is(.nysf-btn--navy .elementor-button, a.nysf-btn--navy, button.nysf-btn--navy, input.nysf-btn--navy) {
	background: var(--nysf-navy);
	color: var(--nysf-light);
}

:is(.nysf-btn--navy .elementor-button, a.nysf-btn--navy, button.nysf-btn--navy, input.nysf-btn--navy)::after {
	background: var(--nysf-gold);
	color: var(--nysf-navy);
}

/* On photography and navy panels: outlined, not filled. */
:is(.nysf-btn--light .elementor-button, a.nysf-btn--light, button.nysf-btn--light, input.nysf-btn--light) {
	background: transparent;
	color: var(--nysf-light);
	box-shadow: inset 0 0 0 1.5px rgba(251, 243, 228, 0.6);
}

:is(.nysf-btn--light .elementor-button, a.nysf-btn--light, button.nysf-btn--light, input.nysf-btn--light)::after {
	background: var(--nysf-light);
	color: var(--nysf-navy);
}

:is(.nysf-btn--light .elementor-button, a.nysf-btn--light, button.nysf-btn--light, input.nysf-btn--light):hover {
	background: rgba(251, 243, 228, 0.12);
	box-shadow: inset 0 0 0 1.5px var(--nysf-light);
}

/* brand.css .btn-ghost is a 1.5px navy outline, not the pale card rule this
   was using, which all but disappeared on cream. */
:is(.nysf-btn--ghost .elementor-button, a.nysf-btn--ghost, button.nysf-btn--ghost, input.nysf-btn--ghost) {
	background: transparent;
	color: var(--nysf-navy);
	box-shadow: inset 0 0 0 1.5px var(--nysf-navy);
}

:is(.nysf-btn--ghost .elementor-button, a.nysf-btn--ghost, button.nysf-btn--ghost, input.nysf-btn--ghost)::after {
	background: var(--nysf-navy);
	color: var(--nysf-light);
}

/*
 * Link states, and why they need their own block.
 *
 * Elementor's frontend.css carries
 *   .elementor-button:focus,
 *   .elementor-button:hover,
 *   .elementor-button:visited { color: #fff }
 * That is a class plus a pseudo-class, so it ties with
 * `.nysf-btn--ghost .elementor-button` and, loading later, wins. The effect
 * is that a ghost or gold button turns its label white the moment you hover
 * it or once the destination has been visited: on cream, invisible. It only
 * shows up after you have clicked something, which is why it survived
 * several review passes.
 *
 * Adding a state pseudo-class of our own takes these to three components and
 * settles it. The hover treatments are brand.css's.
 */
:is(.nysf-btn--gold .elementor-button, a.nysf-btn--gold, button.nysf-btn--gold, input.nysf-btn--gold):is(:link, :visited, :hover, :focus) {
	color: var(--nysf-navy);
}

:is(.nysf-btn--navy .elementor-button, a.nysf-btn--navy, button.nysf-btn--navy, input.nysf-btn--navy):is(:link, :visited, :hover, :focus) {
	color: var(--nysf-light);
}

:is(.nysf-btn--navy .elementor-button, a.nysf-btn--navy, button.nysf-btn--navy, input.nysf-btn--navy):hover {
	background: #06202c;
}

:is(.nysf-btn--ghost .elementor-button, a.nysf-btn--ghost, button.nysf-btn--ghost, input.nysf-btn--ghost):is(:link, :visited, :focus) {
	color: var(--nysf-navy);
}

:is(.nysf-btn--ghost .elementor-button, a.nysf-btn--ghost, button.nysf-btn--ghost, input.nysf-btn--ghost):hover {
	background: var(--nysf-navy);
	color: var(--nysf-light);
}

:is(.nysf-btn--ghost .elementor-button, a.nysf-btn--ghost, button.nysf-btn--ghost, input.nysf-btn--ghost):hover::after {
	background: var(--nysf-gold);
	color: var(--nysf-navy);
}

:is(.nysf-btn--light .elementor-button, a.nysf-btn--light, button.nysf-btn--light, input.nysf-btn--light):is(:link, :visited, :focus) {
	color: var(--nysf-light);
}

:is(.nysf-btn--light .elementor-button, a.nysf-btn--light, button.nysf-btn--light, input.nysf-btn--light):hover {
	color: var(--nysf-navy);
	background: var(--nysf-light);
}

:is(.nysf-btn--light .elementor-button, a.nysf-btn--light, button.nysf-btn--light, input.nysf-btn--light):hover::after {
	background: var(--nysf-gold);
	color: var(--nysf-navy);
}

/*
 * brand.css .btn-sm: the compact variant used beside a section heading and
 * inside an event row, where the full-size pill would out-weigh the h2.
 */
:is(.nysf-btn--sm .elementor-button, a.nysf-btn--sm, button.nysf-btn--sm, input.nysf-btn--sm) {
	padding: 10px 18px;
	font-size: 14px;
}

:is(.nysf-btn--sm .elementor-button, a.nysf-btn--sm, button.nysf-btn--sm, input.nysf-btn--sm)::after {
	width: 20px;
	height: 20px;
	font-size: 11px;
}

/* ---- The arch: rounded top, squarer bottom, echoing the logo dome ---- */

.nysf-arch-media img {
	border-radius: var(--nysf-arch);
	width: 100%;
	display: block;
	object-fit: cover;
}

/* ---- STEM icon family ---- */

/*
 * The set reads as a family across the full measure, which is why the design
 * spaces them out rather than centring a tight cluster.
 */
.nysf-icon-grid,
.nysf-icon-grid > .e-con-inner,
.nysf-head-row,
.nysf-head-row > .e-con-inner,
.nysf-cta-band,
.nysf-cta-band > .e-con-inner {
	display: flex !important;
	flex-direction: row !important;
	flex-wrap: wrap;
	align-items: center;
}

.nysf-icon-grid,
.nysf-icon-grid > .e-con-inner {
	justify-content: space-between !important;
	gap: 24px;
}

.nysf-head-row,
.nysf-head-row > .e-con-inner,
.nysf-cta-band,
.nysf-cta-band > .e-con-inner {
	justify-content: space-between !important;
	gap: 32px;
}

.nysf-head-row,
.nysf-head-row > .e-con-inner {
	align-items: flex-end;
}

/* Children size to their content in every one of these rows. */
.nysf-icon-grid > *,
.nysf-icon-grid > .e-con-inner > *,
.nysf-head-row > *,
.nysf-head-row > .e-con-inner > *,
.nysf-cta-band > *,
.nysf-cta-band > .e-con-inner > * {
	--width: auto;
	width: auto;
	max-width: none;
}

.nysf-icon-grid .nysf-icon-item {
	text-align: center;
	flex: 1 1 0 !important;
	min-width: 120px;
}

.nysf-icon-grid .nysf-icon-item img {
	width: 88px;
	height: 88px;
	border-radius: 50%;
	margin: 0 auto 12px;
	display: block;
}

.nysf-icon-label {
	font-size: 14px;
	font-weight: 600;
	color: var(--nysf-ink);
	margin: 0;
	text-align: center;
}

/* ---- Section head: supporting line sits beside the heading ---- */

/* brand.css .sec-head h2 */
.nysf-head-row .elementor-heading-title {
	font-size: clamp( 32px, 3.6vw, 46px );
	line-height: 1.06;
	max-width: 560px;
}

/*
 * brand.css .sec-head p
 *
 * Left-aligned, not right. The design's .sec-head is a flex row with
 * space-between, so the block sits on the right while its text stays ragged
 * on the right edge like every other paragraph. Setting text-align: right
 * put the ragged edge on the wrong side.
 */
.nysf-head-aside {
	font-size: 16px;
	color: var(--nysf-ink-soft);
	margin: 0 0 6px;
	max-width: 380px;
}

.nysf-panel-dark .nysf-head-aside,
.nysf-panel-dark .nysf-head-aside p {
	color: var(--nysf-light);
	opacity: 0.92;
}

/* ---- Checklist marker: the brand star, not a tick in a circle ---- */

.nysf-checklist li::before {
	content: '✦';
	position: absolute;
	left: 0;
	top: 0;
	width: auto;
	height: auto;
	background: none;
	border-radius: 0;
	color: var(--nysf-gold);
	font-size: 15px;
	line-height: 1.6;
}

.nysf-checklist li::after {
	content: none;
}

/* ---- CTA band ---- */

/* brand.css .cta-band h2 */
.nysf-cta-band .elementor-heading-title {
	margin: 0;
	font-size: clamp( 30px, 3.4vw, 44px );
	line-height: 1.06;
	max-width: 600px;
	color: var(--nysf-navy);
}

/*
 * brand.css .cta-band is padding:62px, set on the container in the builder.
 * A `padding-block: 36px !important` used to live here and silently beat it,
 * because Elementor emits container padding as --padding-* custom properties
 * consumed by an unprefixed rule. Only the small-screen override stays, and
 * it needs !important for the same reason.
 */
@media (max-width: 620px) {
	.nysf-cta-band {
		padding: 38px 26px !important;
	}
}

.nysf-cta-band :is(.nysf-btn .elementor-button, a.nysf-btn, button.nysf-btn, input.nysf-btn) {
	padding: 14px 24px;
}

/* ---- Dark panel ---- */

.nysf-panel-dark .elementor-widget-text-editor,
.nysf-panel-dark p {
	color: rgba(251, 243, 228, 0.85);
}


/* ==========================================================================
   Elementor container width resets

   An Elementor container sets --width: 100% on every child container. That
   means a parent set to flex-direction: row still renders its children
   stacked, which is why the icon grid, the section heading rows and the CTA
   band all came out vertical despite being row containers. These resets let
   the flex layout actually do its job.
   ========================================================================== */

/*
 * Both selector shapes on purpose. A boxed container wraps its children in
 * .e-con-inner, so `> .e-con` misses them; the row containers are now set to
 * full width, which removes that wrapper, but the inner-aware selectors stay
 * so hand-edits in the editor cannot silently break the layout again.
 */
.nysf-icon-grid > .e-con,
.nysf-icon-grid > .e-con-inner > .e-con,
.nysf-head-row > .e-con,
.nysf-head-row > .e-con-inner > .e-con,
.nysf-head-row > .elementor-widget,
.nysf-head-row > .e-con-inner > .elementor-widget,
.nysf-cta-band > .e-con,
.nysf-cta-band > .e-con-inner > .e-con,
.nysf-cta-band > .elementor-widget,
.nysf-cta-band > .e-con-inner > .elementor-widget,
.nysf-btn-row > .elementor-widget,
.nysf-btn-row > .e-con-inner > .elementor-widget {
	--width: auto;
	width: auto;
	max-width: none;
	flex: 0 0 auto;
}

/* The split stays a true two-up until tablet. */
/* brand.css .split is two equal columns with a 48px gap. */
.nysf-split > .e-con,
.nysf-split > .e-con-inner > .e-con {
	--width: 50%;
	flex: 1 1 340px;
}

/* The gap itself comes from the builder, because Elementor writes it per
   element and would outrank anything set here. */
.nysf-split,
.nysf-split > .e-con-inner {
	align-items: center;
}

/* brand.css .split h2 */
.nysf-split .elementor-heading-title {
	font-size: clamp( 30px, 3.4vw, 42px );
	margin-bottom: 16px;
}

/*
 * brand.css stacks .split at 620px, not before.
 *
 * The old rule here was `flex-direction: column` on `.nysf-split` and
 * `--width: 100%` on the children, and neither did anything. The section is
 * a boxed container, so the row lives on its `.e-con-inner`, not on
 * `.nysf-split` itself; and 100% + 100% in a nowrap row just shrinks both
 * back to half. Setting `--flex-wrap` on the section works because custom
 * properties inherit down to the inner element that reads it, and nothing
 * writes `--flex-wrap` per element for a split.
 */
@media (max-width: 620px) {
	.nysf-split {
		--flex-wrap: wrap;
	}

	.nysf-split.e-con > .e-con,
	.nysf-split.e-con > .e-con-inner > .e-con {
		--width: 100%;
	}
}

/* Give the CTA copy room to breathe while the button hugs the right. */
.nysf-cta-band > .e-con:first-child,
.nysf-cta-band > .e-con-inner > .e-con:first-child {
	flex: 1 1 auto;
}

.nysf-head-row > .e-con:first-child,
.nysf-head-row > .e-con-inner > .e-con:first-child {
	flex: 1 1 auto;
}

/* ---- Fighting the parent theme ----
   Inlingua styles img and .elementor-button broadly enough to win on
   specificity, so these two need to be explicit. */

.nysf-arch-media img,
.elementor-widget-image.nysf-arch-media img {
	border-radius: var(--nysf-arch) !important;
	width: 100%;
}

/* Elementor draws its own play button; ours would double up. */
.nysf-arch-media.elementor-widget-video a::after {
	content: none;
}

/*
 * Crop the inline poster to the design's 4:5 arch. Deliberately scoped to the
 * widget rather than done through --video-aspect-ratio, because the lightbox
 * video reads that same variable and the footage is landscape: cropping it to
 * portrait in the lightbox would cut the frame in half.
 */
/*
 * brand.css .split .ph is `aspect-ratio: 1/1; max-height: 480px`. The 4:5 here
 * was a misread of the instructor portrait, which is the one that is 4:5.
 */
.nysf-arch-media .elementor-wrapper {
	aspect-ratio: 1 / 1 !important;
	max-height: 480px;
	border-radius: var(--nysf-arch) !important;
	overflow: hidden;
	width: 100%;
}

.nysf-arch-media .elementor-custom-embed-image-overlay img {
	aspect-ratio: 1 / 1 !important;
	object-fit: cover;
	width: 100%;
	height: 100%;
}

.nysf-arch-media .elementor-custom-embed-play i,
.nysf-arch-media .elementor-custom-embed-play svg {
	color: var(--nysf-gold);
	filter: drop-shadow( 0 10px 24px rgba(9, 41, 56, 0.5) );
}

/* ==========================================================================
   Video block

   brand.css .videoblock: a full-width poster with a navy scrim and a 90px
   gold play button. Elementor's video widget supplies the overlay, the play
   control and the lightbox; this restyles all three. Scoped away from
   .nysf-arch-media, which is the portrait-cropped variant used inside split
   sections.
   ========================================================================== */

.nysf-videoblock .elementor-wrapper {
	position: relative;
	border-radius: var(--nysf-r-lg);
	overflow: hidden;
	min-height: 420px;
}

.nysf-videoblock .elementor-custom-embed-image-overlay img {
	width: 100%;
	height: 100%;
	min-height: 420px;
	object-fit: cover;
	display: block;
}

/* brand.css .videoblock::after */
.nysf-videoblock .elementor-custom-embed-image-overlay::after {
	content: '';
	position: absolute;
	inset: 0;
	background: rgba(9, 41, 56, 0.45);
	pointer-events: none;
}

/*
 * The design's play control is a filled gold disc with a navy triangle, not
 * Elementor's outlined glyph. Drawing the disc on the wrapper and colouring
 * the glyph navy is less brittle than replacing the icon markup.
 */
.nysf-videoblock .elementor-custom-embed-play {
	z-index: 2;
}

.nysf-videoblock .elementor-custom-embed-play::before {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 90px;
	height: 90px;
	margin: -45px 0 0 -45px;
	border-radius: 50%;
	background: var(--nysf-gold);
	/* brand.css: a slow gold ripple out of the disc. */
	box-shadow: 0 0 0 0 rgba( 239, 175, 58, 0.6 );
	animation: nysf-play-pulse 2.2s infinite;
}

@keyframes nysf-play-pulse {
	0% {
		box-shadow: 0 0 0 0 rgba( 239, 175, 58, 0.5 );
	}

	70% {
		box-shadow: 0 0 0 26px rgba( 239, 175, 58, 0 );
	}

	100% {
		box-shadow: 0 0 0 0 rgba( 239, 175, 58, 0 );
	}
}

@media (prefers-reduced-motion: reduce) {
	.nysf-videoblock .elementor-custom-embed-play::before {
		animation: none;
	}
}

/*
 * Elementor's glyph is an outlined circle with a triangle inside it, sitting
 * inside our gold disc: a ring within a ring. The design is a solid navy
 * triangle and nothing else. Hiding the icon and drawing the design's own
 * path is the same move as the carousel arrows, and avoids depending on the
 * eicons font loading at all.
 */
.nysf-videoblock .elementor-custom-embed-play i,
.nysf-videoblock .elementor-custom-embed-play svg {
	display: none;
}

.nysf-videoblock .elementor-custom-embed-play::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 30px;
	height: 30px;
	/* Optically centred: a triangle's visual centre sits left of its box. */
	transform: translate( -50%, -50% );
	margin-left: 5px;
	background: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23092938'%3E%3Cpath d='M8 5.2v13.6L19 12z'/%3E%3C/svg%3E" ) center / contain no-repeat;
}

/* ==========================================================================
   Logo ticker caption
   ========================================================================== */

.nysf-ticker-caption {
	text-align: center;
	color: var(--nysf-ink-soft);
	font-size: 14px;
	letter-spacing: 0.4px;
	margin: 0 0 18px;
}

/* ==========================================================================
   Guided entry overlap

   The prototype's own page CSS is `.pf-overlap { position: relative; z-index:
   5; margin-top: -86px }`, pulling the panel up over the foot of the hero.
   The negative margin is set on the container in the builder so it is visible
   in the editor; this is the stacking context that keeps it above the hero
   image rather than behind it.
   ========================================================================== */

.nysf-pf-overlap {
	position: relative;
	z-index: 5;
}

@media (max-width: 620px) {
	/* Below the design's breakpoint the panel sits under the hero, not over it. */
	.nysf-pf-overlap {
		margin-top: 28px !important;
	}
}

/* Our arrow is the ::after circle. Anything the theme or Elementor injects
   ahead of the label duplicates it. */
.nysf-btn .elementor-button-icon,
.nysf-btn .elementor-button i,
.nysf-btn .elementor-button svg {
	display: none !important;
}

.nysf-btn .elementor-button-content-wrapper {
	display: inline-flex;
	align-items: center;
	gap: 10px;
}

.nysf-btn-row,
.nysf-btn-row > .e-con-inner {
	justify-content: flex-start;
}

/* ==========================================================================
   Panel insets

   A boxed Elementor container is only capped on its .e-con-inner: the element
   itself spans the viewport, so any background on it paints edge to edge.
   The design has these as panels sitting inside the margins, so they are set
   to full width and capped here instead.
   ========================================================================== */

/*
 * Use Elementor's own boxed measure rather than our token, so an inset panel
 * lines up exactly with the text sections above and below it. Hardcoding
 * 1240px left the panels visibly wider than the copy.
 */
/*
 * `min(100%, ...)` alone meant that below 1184px the panel filled the viewport
 * edge to edge, so its 34px corners ran off the screen on tablet. The design
 * keeps these inside .wrap, which is a 28px gutter (18px at <=620).
 */
.nysf-inset {
	max-width: min(calc(100% - 56px), var(--container-max-width, 1184px)) !important;
	margin-inline: auto !important;
}

@media (max-width: 620px) {
	.nysf-inset {
		max-width: calc(100% - 36px) !important;
	}
}

/* ---- Stat band: four equal columns, brand.css .statband .stats-grid ---- */

/*
 * Scoped under .nysf-statband so this outranks Elementor's own
 * `.e-con { display: flex }` without !important.
 */
.nysf-statband .nysf-statband-grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	text-align: center;
}

/*
 * Not every band has four stats: support-nysf has two. A fixed four-column
 * grid left those clustered against the left edge with two empty tracks, so
 * the builder writes the count onto the element.
 */
.nysf-statband .nysf-statband-grid--2 {
	grid-template-columns: repeat(2, 1fr);
}

.nysf-statband .nysf-statband-grid--3 {
	grid-template-columns: repeat(3, 1fr);
}

.nysf-statband .nysf-statband-grid--5 {
	grid-template-columns: repeat(5, 1fr);
}

/*
 * The counter here is Inlingua's, not Elementor core's: the theme registers a
 * widget also named `counter`, so it replaces core's. Its stylesheet declares
 * `font-size: 82px` on .elementor-counter-number and the prefix/suffix spans
 * *directly*, so setting a size on .elementor-counter-number-wrapper does
 * nothing: an explicit declaration on the child always beats an inherited
 * value. Target the spans.
 */
.nysf-statband .elementor-counter-number,
.nysf-statband .elementor-counter-number-prefix,
.nysf-statband .elementor-counter-number-suffix {
	font-family: var(--nysf-display);
	font-weight: 700;
	font-size: 52px;
	line-height: 1;
}

/*
 * .elementor-counter is `display:flex; flex-direction:column; width:fit-content`,
 * so it hugs the left of its grid cell and inherited text-align cannot centre
 * it. Fill the cell and centre the column instead.
 */
.nysf-statband .elementor-counter {
	width: 100%;
	align-items: center;
	text-align: center;
}

.nysf-statband .elementor-counter-number-wrapper {
	display: flex;
	justify-content: center;
}

/* Colour comes from the widget's own title_color control, which outranks this. */
.nysf-statband .elementor-counter-title {
	font-size: 15px;
	line-height: 1.35;
	margin-top: 8px;
}

/* brand.css drops the stat grid at 980 and 620, not 900 and 560. */
@media (max-width: 980px) {
	.nysf-statband .nysf-statband-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 620px) {
	.nysf-statband .nysf-statband-grid {
		grid-template-columns: 1fr;
	}
}

/*
 * The molecule is one centred cover image, not a tile, run at 0.4 on the
 * programs panel and 0.5 on the stat band. Those values are set through
 * Elementor's own overlay controls: for containers the overlay is a ::before
 * pseudo-element, so there is no element to target by class here.
 */

/* ---- Split media: the design's arch is portrait, not landscape ---- */

.elementor-widget-image.nysf-arch-media img {
	aspect-ratio: 4 / 5;
	object-fit: cover;
	max-height: 560px;
}

@media (max-width: 880px) {
	.nysf-arch-media img,
	.elementor-widget-image.nysf-arch-media img {
		aspect-ratio: 3 / 2;
	}
}

/* ---- Video poster: make it read as playable ---- */

.nysf-arch-media {
	position: relative;
}

.nysf-arch-media a::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 68px;
	height: 68px;
	transform: translate(-50%, -50%);
	border-radius: 50%;
	background: var(--nysf-gold) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='26' viewBox='0 0 24 24' fill='%23092938'%3E%3Cpath d='M8 5.2v13.6L19 12z'/%3E%3C/svg%3E") no-repeat center;
	box-shadow: 0 10px 30px -8px rgba(9, 41, 56, 0.6);
	transition: 0.25s;
}

.nysf-arch-media a:hover::after {
	transform: translate(-50%, -50%) scale(1.06);
}

/* ==========================================================================
   Hero
   ========================================================================== */

/*
 * Hero type, straight from brand.css (.hero-full). The h1 running to 72px
 * inside a 640px measure is what makes it break after "a future"; at the 58px
 * I had guessed earlier it fitted on one line and lost the shape of the
 * design. Tracking is -1.5px here rather than the -0.5px other headings use.
 */
.nysf-hero .elementor-heading-title {
	max-width: 640px;
	font-size: clamp( 40px, 5.6vw, 72px );
	line-height: 1.02;
	letter-spacing: -1.5px;
}

.nysf-crumb {
	font-size: 14px;
	color: #9db3bc;
	margin: 0 0 14px;
}

.nysf-crumb a {
	color: var(--nysf-light);
	font-weight: 600;
	text-decoration: none;
}

/*
 * brand.css stops at the colour, which leaves a crumb link looking like
 * emphasised text until you happen to mouse over it. The underline only
 * appears on hover, so the resting state still matches the design.
 */
.nysf-crumb a:hover,
.nysf-crumb a:focus-visible {
	color: var(--nysf-gold);
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* ==========================================================================
   Page banner

   brand.css .page-banner: a navy band with everything centred, not the
   photographic hero. Thirteen inner pages use it.
   ========================================================================== */

.nysf-banner {
	position: relative;
	/*
	 * Fallback only. The clipping that matters is the container's Overflow
	 * control, set in section_banner(): Elementor declares
	 * `.e-con { overflow: var(--overflow) }` at one class, so this ties on
	 * specificity, loses on load order, and the solar mark bleeds into the
	 * section below.
	 */
	overflow: hidden;
	text-align: center;
}

/*
 * Every child is centred, including the button row, which is a flex container
 * of its own and so ignores text-align.
 */
.nysf-banner > .e-con-inner > * {
	margin-inline: auto;
}

.nysf-banner .nysf-btn-row,
.nysf-banner .nysf-btn-row > .e-con-inner {
	justify-content: center;
}

.nysf-banner .elementor-heading-title {
	position: relative;
	font-size: clamp( 40px, 5vw, 64px );
	line-height: 1.04;
	max-width: none;
}

.nysf-banner-lead {
	position: relative;
	color: #c3d1d7;
	max-width: 560px;
	margin: 14px auto 0;
}

/*
 * Ornament, so pseudo-elements rather than empty containers in the element
 * tree. shape-a is the solar mark bleeding off the right edge; shape-b is the
 * short gold rule at the bottom left.
 */
/*
 * The compound selector is load-bearing, not decoration.
 *
 * Elementor already owns ::before on every container, for the background
 * overlay: frontend.min.css declares
 *   .e-con:before { content: var(--background-overlay); width: max(100%...);
 *                   height: max(100%...); top: ...; left: ... }
 * With no overlay configured, --background-overlay is undefined, so `content`
 * is invalid at computed-value time and resolves to `normal`, which means no
 * pseudo-element is generated at all. That rule is a single class, exactly the
 * same specificity as `.nysf-banner::before`, and Elementor's stylesheet loads
 * after ours, so it won and the solar mark never appeared. The bare selector
 * stays alongside for any use of .nysf-banner outside a container.
 *
 * `left: auto` is needed for the same reason: Elementor sets `left`, and
 * setting only `right` would leave both edges pinned and stretch the shape.
 */
.nysf-banner::before,
.nysf-banner.e-con::before {
	content: '';
	position: absolute;
	left: auto;
	right: -170px;
	top: 50%;
	width: 520px;
	height: 520px;
	margin-top: -260px;
	background: url('../img/solar.svg') center / contain no-repeat;
	animation: nysf-solarspin 80s linear infinite;
	pointer-events: none;
	z-index: 0;
}

.nysf-banner::after,
.nysf-banner.e-con::after {
	content: '';
	position: absolute;
	left: -30px;
	bottom: 0;
	width: 150px;
	height: 6px;
	background: var(--nysf-gold);
	pointer-events: none;
	z-index: 1;
}

/* The copy sits above the solar mark, which bleeds behind it. */
.nysf-banner > .e-con-inner {
	position: relative;
	z-index: 2;
}

@keyframes nysf-solarspin {
	from {
		transform: rotate(0deg);
	}

	to {
		transform: rotate(360deg);
	}
}

@media (max-width: 980px) {
	.nysf-banner::before {
		display: none;
	}
}

@media (prefers-reduced-motion: reduce) {
	.nysf-banner::before {
		animation: none;
	}
}

.nysf-hero-badge,
.nysf-hero-badge .elementor-widget-container,
.nysf-hero-badge figure {
	text-align: left;
	margin-inline: 0;
}

.nysf-hero-badge img {
	width: 56px;
	height: 56px;
	border-radius: 50%;
	margin: 0 0 6px;
}

/* ==========================================================================
   Hero scrim

   Straight from the prototype: a four-stop horizontal gradient, 90% opaque at
   the left edge falling to 4% at the right, so the copy has contrast while the
   photograph stays visible on the right. Elementor's overlay control tops out
   at two stops, so the element it renders is repurposed here.
   ========================================================================== */

/*
 * Containers render their overlay as ::before, not as an
 * .elementor-background-overlay div (that is section-era markup). Targeting
 * the div matched nothing, which left the configured flat navy overlay at full
 * opacity sitting on top of the photograph and hiding it entirely.
 */
.nysf-hero::before {
	background-image: linear-gradient(
		90deg,
		rgba(9, 41, 56, 0.9) 0%,
		rgba(9, 41, 56, 0.66) 40%,
		rgba(9, 41, 56, 0.18) 74%,
		rgba(9, 41, 56, 0.04) 100%
	) !important;
	background-color: transparent !important;
	opacity: 1 !important;
}

.nysf-hero {
	min-height: 72vh;
}

/*
 * Phone: the scrim turns through 90 degrees.
 *
 * The desktop gradient runs left to right because the copy sits in the left
 * third and the photograph is meant to stay visible on the right. On a phone
 * the copy spans the full width, so it crossed the pale end of that gradient
 * and the lead paragraph ended up as cream text over a silver dome and a
 * crowd in white shirts. Unreadable, and the first thing you see on twelve of
 * the twenty-six pages.
 *
 * brand.css does turn it vertical at this width, at .5 to .85, but those
 * values do not survive being measured against this particular photograph.
 * Over the brightest part of it:
 *
 *   scrim .50  lead 2.39:1   eyebrow 1.62:1
 *   scrim .85  lead 7.27:1   eyebrow 4.93:1
 *
 * The eyebrow is 13px bold, so it needs 4.5:1, and it sits 11% down the hero
 * where the design's gradient is still near its lightest. Hence a faster ramp
 * than the design specifies: full strength by 12%, just above where the
 * content starts, and deepening to the foot.
 *
 * The trade is that the mobile hero is closer to a dark panel with a texture
 * behind it than to a photograph. On a phone, with nine lines of copy over
 * it, that is the right way round. The alternative is a shorter hero lead on
 * mobile, which is a content decision for Good Apples rather than a CSS one.
 */
@media (max-width: 620px) {
	.nysf-hero::before {
		background-image: linear-gradient(
			180deg,
			rgba(9, 41, 56, 0.4) 0%,
			rgba(9, 41, 56, 0.88) 12%,
			rgba(9, 41, 56, 0.95) 100%
		) !important;
	}
}

/* Design measures: h1 to 640, lead to 500 at 19px. */
.nysf-hero .nysf-lead {
	max-width: 500px;
	font-size: 19px;
	line-height: 1.5;
	color: #e9e0cf;
	margin: 20px 0 32px;
}


.nysf-hero-badge img {
	box-shadow: 0 2px 10px rgba(9, 41, 56, 0.25);
}

/* The programs panel dims its supporting line rather than using full light. */
.nysf-panel-dark .nysf-head-aside,
.nysf-panel-dark .nysf-head-aside p {
	color: #c7d2d6;
	opacity: 1;
}

/* ==========================================================================
   Testimonials

   Configuration of the theme's inlingua-testimonials widget, restyled to
   brand.css section 12 (.quote): Solar Flare card, gold quote mark, Fraunces
   italic quote, arch avatar.

   Rendered as the widget's plain grid, not its carousel. See
   section_testimonials() in class-page-builder.php for why.

   Markup from inc/elementor/widgets/testimonial.php layout 1:
     .elementor-testimonial-item-wrapper.inlingua-wrapper
       > .inlingua-con
         > .inlingua-con-inner.layout-1.elementor-grid
           > .elementor-grid-item.elementor-testimonial-item
             > .item-inner > .item-inner.inner
               > .testimonial-content-text
                 > .testimonial-caption > .caption-top (image)
                                        > .caption-bottom > .details > .name, .job
                 > .content (quote)
   ========================================================================== */

/* brand.css .quote */
.nysf-testimonials .elementor-testimonial-item .item-inner.inner {
	position: relative;
	/*
	 * The ink variant, because this panel carries 13.5px and 16px text.
	 * On the brand swatch that measured 4.45:1 and 3.63:1.
	 */
	background: var(--nysf-flare-ink);
	color: var(--nysf-light);
	border-radius: 22px;
	padding: 32px 28px;
	box-shadow: var(--nysf-shadow-sm);
	height: 100%;
}

/*
 * Layout 1 renders the avatar above the quote. The design puts the quote
 * first, so the two children are reordered rather than switching layout,
 * which would change other things as well. The ::before quote mark keeps
 * the default order of 0 and so leads.
 */
.nysf-testimonials .testimonial-content-text {
	display: flex;
	flex-direction: column;
	height: 100%;
}

.nysf-testimonials .testimonial-content-text .content {
	order: 1;
}

.nysf-testimonials .testimonial-caption {
	order: 2;
	margin-top: auto;
	display: flex;
	align-items: center;
	gap: 12px;
}

/* brand.css .quote .qm */
.nysf-testimonials .testimonial-content-text::before {
	content: '\201C';
	display: block;
	font-family: var(--nysf-quote);
	font-style: italic;
	font-size: 60px;
	line-height: 0.6;
	height: 34px;
	color: var(--nysf-gold);
}

/* brand.css .quote p */
.nysf-testimonials .content {
	font-family: var(--nysf-quote);
	font-style: italic;
	font-size: 18px;
	line-height: 1.5;
	color: var(--nysf-light);
	margin-bottom: 22px;
}

/* brand.css .quote .who img: the arch, not a circle. */
.nysf-testimonials .caption-top img {
	width: 48px;
	height: 48px;
	border-radius: 120px 120px 8px 8px;
	object-fit: cover;
	background: var(--nysf-twilight);
	display: block;
}

.nysf-testimonials .details {
	display: flex;
	flex-direction: column;
	line-height: 1.25;
}

.nysf-testimonials .details .name {
	font-family: var(--nysf-display);
	font-weight: 600;
	font-size: 16px;
	color: var(--nysf-light);
	font-style: normal;
}

.nysf-testimonials .details .job {
	font-size: 13.5px;
	/*
	 * Full opacity, not 0.85. At 13.5px the faded version measured 3.63:1
	 * on the flare panel and 4.18:1 even after the panel moved to the ink
	 * variant. The role line is the second most useful thing in a
	 * testimonial after the quote; fading it was decoration.
	 */
	color: var(--nysf-light);
	font-style: normal;
}

/*
 * Without the carousel the widget falls back to .elementor-grid, which the
 * theme lays out from --e-global-column-to-show. Stating the grid here keeps
 * the three cards equal-width and equal-height regardless of that variable.
 */
.nysf-testimonials .inlingua-con-inner {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 24px;
	align-items: stretch;
}

.nysf-testimonials .elementor-testimonial-item,
.nysf-testimonials .elementor-testimonial-item > .item-inner {
	height: 100%;
}

/*
 * No testimonial-specific breakpoints here any more. They used to sit at
 * (0,2,0) and never fired, because Elementor writes the column count per
 * element at a higher specificity. The general post-grid rule further down
 * this file handles the testimonials along with every other grid.
 */

/* ==========================================================================
   Document pages

   Privacy and Terms. brand.css `.doc` is a 240px contents nav beside the
   body, collapsing to one column at 820px; `.richtext` is a 760px measure.
   The body is a single rich text block so the h2s, lists and inline links
   survive and NYSF can edit it as a document.
   ========================================================================== */

.nysf-doc {
	display: grid;
	grid-template-columns: 240px 1fr;
	gap: 44px;
	align-items: start;
}

.nysf-doc--plain {
	grid-template-columns: 1fr;
}

.nysf-toc {
	background: var(--nysf-card);
	border: 1px solid var(--nysf-line);
	border-radius: var(--nysf-r-md);
	padding: 22px;
	position: sticky;
	top: 100px;
}

.nysf-toc h6 {
	font-family: var(--nysf-display);
	font-size: 14px;
	margin: 0 0 12px;
}

.nysf-toc a {
	display: block;
	color: var(--nysf-ink-soft);
	font-size: 14px;
	text-decoration: none;
	padding: 6px 0;
}

.nysf-toc a:hover,
.nysf-toc a:focus-visible {
	color: var(--nysf-flare);
}

.nysf-richtext {
	max-width: 760px;
}

.nysf-doc--plain .nysf-richtext {
	margin-inline: auto;
}

.nysf-richtext h2 {
	font-family: var(--nysf-display);
	font-size: 26px;
	margin: 34px 0 14px;
	/* The contents nav jumps to these, and the header is sticky. */
	scroll-margin-top: 110px;
}

.nysf-richtext h3 {
	font-family: var(--nysf-display);
	font-size: 19px;
	margin: 24px 0 10px;
	scroll-margin-top: 110px;
}

.nysf-richtext p {
	color: var(--nysf-ink-soft);
	margin-bottom: 16px;
}

.nysf-richtext ul {
	color: var(--nysf-ink-soft);
	margin: 0 0 16px 22px;
	display: grid;
	gap: 8px;
}

.nysf-richtext a {
	color: var(--nysf-flare);
	font-weight: 600;
}

.nysf-richtext .updated {
	color: var(--nysf-ink-soft);
	font-size: 14px;
	font-style: italic;
	margin-bottom: 26px;
}

@media (max-width: 820px) {
	.nysf-doc {
		grid-template-columns: 1fr;
	}

	.nysf-toc {
		position: static;
	}
}

/* ==========================================================================
   Forms

   Contact Form 7's own markup, restyled to brand.css `.field`. CF7 wraps
   every control in a `<span class="wpcf7-form-control-wrap">`, so the
   selectors reach through it rather than styling the input directly.
   ========================================================================== */

.nysf-field {
	margin-bottom: 16px;
}

.nysf-field label {
	display: block;
	font-weight: 600;
	font-size: 14px;
	margin-bottom: 7px;
	color: var(--nysf-navy);
}

.nysf-field .wpcf7-form-control-wrap {
	display: block;
}

.nysf-field :is(input[type='text'], input[type='email'], input[type='tel'], input[type='url'], textarea, select) {
	width: 100%;
	padding: 14px 16px;
	border: 1px solid var(--nysf-line);
	border-radius: 14px;
	background: var(--nysf-card);
	font-family: var(--nysf-sans);
	font-size: 15px;
	color: var(--nysf-navy);
	/*
	 * No `outline: none`.
	 *
	 * It was here to suppress the browser's default ring in favour of
	 * the styled focus state below. That is a fair trade only while the
	 * replacement is guaranteed, and it is exactly the habit that left
	 * the rest of the site with no indicator at all. The global
	 * :focus-visible rule in chrome.css now draws the ring, and if that
	 * ever fails the browser default is better than nothing.
	 */
	transition: 0.2s;
}

.nysf-field textarea {
	min-height: 120px;
	resize: vertical;
}

.nysf-field :is(input, textarea, select):focus {
	border-color: var(--nysf-gold);
	box-shadow: 0 0 0 3px rgba(239, 175, 58, 0.2);
}

.nysf-field-row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 16px;
}

@media (max-width: 620px) {
	.nysf-field-row {
		grid-template-columns: 1fr;
	}
}

/*
 * Inlingua sizes every Elementor button, and outranks the rules above.
 *
 * `assets/css/base/elementor.css` carries
 *   .elementor-widget-button .elementor-button.elementor-size-md {
 *       font-size: 18px; padding: 17px 34px 16px 35px }
 * for every size class. That is three classes against the two-class rules
 * above, so it wins on specificity no matter what order the stylesheets
 * load in, and every Elementor button on the site has been rendering at
 * 18px with 34px of side padding instead of the design's 15px and 26px.
 *
 * It only became visible on How to apply, where two buttons share a 568px
 * column: at the design's size they total 541px and sit on one line, at
 * Inlingua's they total 674px and wrap. Everywhere else it just made the
 * buttons quietly too big.
 *
 * Four classes settles it. The attribute selector covers whichever size
 * class the widget happens to carry rather than naming `md`.
 */
.nysf-btn.elementor-widget-button .elementor-button[class*='elementor-size-'] {
	font-size: 15px;
	padding: 15px 26px;
}

.nysf-btn--sm.elementor-widget-button .elementor-button[class*='elementor-size-'] {
	font-size: 14px;
	padding: 10px 18px;
}

.nysf-cta-solo .nysf-btn.elementor-widget-button .elementor-button[class*='elementor-size-'] {
	font-size: 16px;
	padding: 17px 34px;
}

/*
 * Inlingua also puts `margin-left: 6px` on the button's :after, on top of
 * the 10px flex gap. brand.css has only the gap, so every button was 6px
 * wider than the design.
 */
.nysf-btn.elementor-widget-button .elementor-button::after,
:is(a, button, input).nysf-btn::after {
	margin-left: 0;
}

/*
 * A full-size ghost button carries no arrow.
 *
 * It is the quieter half of a pair — "Access & Equity Scholarships" beside
 * "Find your local Rotary club", "Register now" beside "Explore programs" —
 * and the prototype gives the circle arrow only to the primary. Ours drew
 * one on everything, which cost 36px and is what tipped the How to apply
 * pair over its 568px column and onto two lines.
 *
 * The small variant keeps its arrow: that is the "All stories" treatment
 * beside a section heading, where the design does show one.
 */
.nysf-btn--ghost:not(.nysf-btn--sm) .elementor-button::after,
:is(a, button, input).nysf-btn--ghost:not(.nysf-btn--sm)::after {
	content: none;
}

/*
 * Contact Form 7's submit is an `<input type="submit">`.
 *
 * Every .nysf-btn rule above was written as `.nysf-btn .elementor-button` or
 * `a.nysf-btn`, because until now every button on the site was one or the
 * other. The CF7 submit matched neither and rendered as a default grey
 * browser button. The `:is()` lists now cover <button> and <input> too.
 *
 * An <input> cannot carry a pseudo-element, so it gets no arrow.
 */
.nysf-form__submit {
	width: 100%;
	justify-content: center;
	border: 0;
	cursor: pointer;
}

.nysf-form__submit::after {
	content: none;
}

/* CF7's validation and response messages, which ship unstyled. */
.wpcf7-not-valid-tip {
	color: var(--nysf-flare);
	font-size: 13.5px;
	margin-top: 6px;
}

.nysf-field :is(input, textarea, select).wpcf7-not-valid {
	border-color: var(--nysf-flare);
}

.wpcf7-response-output {
	margin: 18px 0 0;
	padding: 14px 18px;
	border: 1px solid var(--nysf-line);
	border-radius: var(--nysf-r-md);
	font-size: 14.5px;
}

.wpcf7 form.invalid .wpcf7-response-output,
.wpcf7 form.unaccepted .wpcf7-response-output {
	border-color: var(--nysf-flare);
}

.wpcf7 form.sent .wpcf7-response-output {
	border-color: var(--nysf-gold);
	background: rgba(239, 175, 58, 0.1);
}

/* ---- The card the form sits in ---- */

.nysf-form-card {
	background: var(--nysf-card);
	border: 1px solid var(--nysf-line);
	box-shadow: var(--nysf-shadow-sm);
	/* Padding and radius are set per element by the builder: Elementor's own
	   `.e-con` rules would beat them from here. */
}

.nysf-form-card .elementor-heading-title {
	font-family: var(--nysf-display);
	font-size: 22px;
	margin-bottom: 20px;
}

/* ---- Contact: details beside the form ---- */

/*
 * The fact box widget again, restyled from one bordered card into a stack of
 * separate rows. Reusing it keeps the icon, label and note markup in one
 * place rather than adding a near-identical widget.
 */
.nysf-cinfo-list .nysf-factbox {
	position: static;
	background: none;
	border: 0;
	border-radius: 0;
	padding: 0;
	box-shadow: none;
}

.nysf-cinfo-list .nysf-factbox__list {
	display: grid;
	gap: 14px;
	margin: 0;
}

.nysf-cinfo-list .nysf-factbox__list li {
	background: var(--nysf-card);
	border: 1px solid var(--nysf-line);
	border-radius: var(--nysf-r-md);
	padding: 18px 20px;
	transition: 0.2s;
}

.nysf-cinfo-list .nysf-factbox__list li:hover {
	border-color: var(--nysf-gold);
}

.nysf-cinfo-list .nysf-factbox__icon {
	flex: 0 0 46px;
	width: 46px;
	height: 46px;
	background: var(--nysf-navy);
	color: var(--nysf-light);
}

/* ---- Partners: the enquiry panel ---- */

.nysf-become {
	background: var(--nysf-navy);
	color: var(--nysf-light);
	overflow: hidden;
}

.nysf-become .nysf-eyebrow {
	color: var(--nysf-gold);
}

.nysf-become__copy :is(h2, .elementor-heading-title) {
	color: var(--nysf-light);
}

.nysf-become__copy .nysf-checklist li {
	color: var(--nysf-light);
}

@media (max-width: 880px) {
	.nysf-become {
		--flex-wrap: wrap;
	}

	.nysf-become.e-con > .e-con,
	.nysf-become.e-con > .e-con-inner > .e-con {
		--width: 100%;
	}
}

/* ---- How to apply: the standalone call to action ---- */

.nysf-cta-solo :is(.nysf-btn .elementor-button, a.nysf-btn, button.nysf-btn, input.nysf-btn) {
	padding: 17px 34px;
	font-size: 16px;
}

.nysf-cta-solo p {
	color: var(--nysf-ink-soft);
	font-size: 14px;
	text-align: center;
	margin-top: 12px;
}

/*
 * The standalone FAQ: head at the content column's left edge, accordion
 * centred under it in a 920px column.
 *
 * The width goes on the widget wrapper, not on `.nysf-accordion` inside it.
 * The wrapper is a flex item in a column container, and `margin-inline: auto`
 * on a flex item makes it shrink to its content rather than stretch — which
 * is what collapsed these cards to about 410px. Giving it a definite width
 * first means the auto margins centre a full-width box instead of sizing it.
 */
.nysf-faq > .e-con-inner > .elementor-widget-nysf-accordion,
.nysf-faq > .elementor-widget-nysf-accordion {
	width: 100%;
	max-width: 920px;
	margin-inline: auto;
}


/* ==========================================================================
   Responsive: the post grid

   Every card grid on the site (program cards, story cards, people grid,
   testimonials) renders through Inlingua's post grid, which puts the column
   count on `.inlingua-con-inner.elementor-grid`.

   Only the desktop column count was ever set, so the widget emits
   `elementor-grid-4` and no `elementor-grid-tablet-N` or
   `elementor-grid-mobile-N` alongside it. Four columns therefore applied at
   every width: at 390px the grid was still computing four tracks inside a
   342px container, and because `overflow-x: clip` on html and body clips
   rather than scrolls, two and a half cards were simply cut off the side of
   the screen with no scrollbar to hint at it.

   Fixed here rather than by setting the responsive controls in the page
   builder, for three reasons: it needs no page rebuild, it covers grids NYSF
   add later in the editor, and 980 and 620 are brand.css's own breakpoints
   so every grid on the site turns over at the same two widths as the design.

   Three classes, because Elementor writes its own value per element at two
   and loads after this file. Measured: it wins without `!important`.
   ========================================================================== */

@media (max-width: 980px) {
	.elementor-widget .inlingua-con-inner.elementor-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 620px) {
	.elementor-widget .inlingua-con-inner.elementor-grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ==========================================================================
   Responsive: the video block

   `.e-hosted-video` carries Elementor's `aspect-ratio: 16 / 9`, and this
   theme gives it `min-height: 420px` to match brand.css. Together those two
   derive the width rather than the height: 420 x 16/9 is 747px, so the block
   rendered 747px wide inside a 342px column and the poster ran off the edge.

   `max-width: 100%` is the belt: whatever the aspect maths asks for, a video
   wrapper should never be wider than the thing containing it. Standing down
   the min-height at the design's breakpoints is the braces, so the block
   keeps roughly its 16:9 shape instead of turning into a tall letterboxed
   box on a phone.

   brand.css does not answer this one. Its `.videoblock` min-height is 420 at
   every width, which the prototype never tested on a phone.
   ========================================================================== */

.nysf-videoblock .elementor-wrapper {
	max-width: 100%;
}

@media (max-width: 980px) {
	.nysf-videoblock .elementor-wrapper,
	.nysf-videoblock .elementor-custom-embed-image-overlay img {
		min-height: 320px;
	}
}

@media (max-width: 620px) {
	.nysf-videoblock .elementor-wrapper,
	.nysf-videoblock .elementor-custom-embed-image-overlay img {
		min-height: 220px;
	}

	/* 90px is a quarter of the width of a 342px block. */
	.nysf-videoblock .elementor-custom-embed-play::before {
		width: 72px;
		height: 72px;
		margin: -36px 0 0 -36px;
	}

	.nysf-videoblock .elementor-custom-embed-play::after {
		width: 24px;
		height: 24px;
		margin-left: 4px;
	}
}
