/* ==========================================================================
   PPSC CHROME — site header, mega menu, alert bar
   Everything that has to work on EVERY page of the site, not just the
   design system pages.

   LOADING
     Sitewide:        ppsc-tokens.css -> ppsc-chrome.css
     New DS pages:    ppsc-tokens.css -> ppsc-chrome.css -> ppsc-ds.css

   The dependency points one way only: ppsc-ds.css assumes this file is
   already loaded, this file never needs ppsc-ds.css. The design
   system's global reset (* { margin: 0; padding: 0 }) stays in
   ppsc-ds.css and never reaches a legacy page.

   WHAT LIVES HERE, AND WHY
     - Header, mega menu, drawer, skip link, backdrop, scroll lock
     - Alert bar
     - The shared primitives the header markup depends on:
       .ppsc-ds-section / .ppsc-ds-container (gutter + max-width),
       the .ppsc-ds-btn family (drawer Apply, mega menu CTA, alert CTA)
     - --ppsc-ds-focus-ring. It is NOT in ppsc-tokens.css. Every header
       :focus-visible outline reads from it and the dark topbar
       redefines it locally, so without this the header has no visible
       focus ring (WCAG 2.4.7).
     - The header/mega/drawer local values, and the shared tagline +
       caps values the mega menu group headings use.
     - body { margin: 0 }. The topbar is full-bleed; a UA body margin
       insets it by 8px. PPSC's sitewide Bootstrap reboot already zeroes
       this, so it is belt-and-braces — but the header does depend on it,
       so it is stated here rather than assumed.

   These rules keep the ppsc-ds- prefix, the same as the header markup.
   ========================================================================== */

/* ============================================================
   LOCAL VALUES
   Figma values with no matching token in ppsc-tokens.css. Every rule
   refers to these names rather than the literal, so replacing one
   with a token is a single edit here.

   The typography and tagline group is shared with ppsc-ds.css. It
   lives here because this is the file that is always present.
   ============================================================ */
:root {
  /* Typography — no text-transform / font-variant tokens */
  --ppsc-ds-text-transform-caps: uppercase;
  --ppsc-ds-font-variant-small-caps: all-small-caps;
  --ppsc-ds-tracking-tagline: 0.02em; /* Taglines M */
  --ppsc-ds-tagline-padding-block: 8px;
  --ppsc-ds-tagline-padding-inline: 12px; /* legacy d-space-xs */

  /* Header / mega menu — Figma frame sizes */
  --ppsc-ds-logo-width: 318px; /* logo artwork */
  --ppsc-ds-logo-height: 22px; /* logo artwork */
  --ppsc-ds-logo-width-s: 260px; /* mobile logo, 260:18 */
  --ppsc-ds-mega-menu-radius: 16px; /* radius-l is 9px */
  --ppsc-ds-mega-menu-feature-width: 416px;
  --ppsc-ds-mega-menu-back-row-height: 50px;
  --ppsc-ds-drawer-max-width: 444px; /* 480 frame − space-l */
  /* Topbar search. Figma's gap on the text input is 12px, which sits
     between space-2xs (9px) and space-xs (14px) — no token matches. */
  --ppsc-ds-header-search-gap: 12px;
  /* Mobile field height. Figma "Topbar / 2" is a 48px bar with
     space-3xs padding, leaving the white field 38px — the dark topbar
     stays visible as a band above and below it rather than being
     covered. Taller than the field's own natural 34px, because in
     Figma it stretches to the bar's content box. */
  --ppsc-ds-header-search-height-s: 38px;
  /* The show/hide fade. ppsc-ds.js reads this value back off the
     element, so this is the only place to change it. */
  --ppsc-ds-header-search-duration: 0.5s;
  /* Drawer slide. The backdrop fade and the visibility flips are keyed
     to the same duration — change all three together or the drawer
     disappears mid-slide. */
  --ppsc-ds-drawer-duration: 0.42s;
  /* Drawer entrance curve: the Material standard curve. It decelerates
     into place without front-loading the movement. */
  --ppsc-ds-drawer-ease: cubic-bezier(0.4, 0, 0.2, 1);
  /* Sub-panel slides inside the drawer: quicker than the drawer, with a
     curve that snaps into place.

     Go back runs the panel out and the list in TOGETHER, so both must
     share these values or the two halves of the cross-slide drift. */
  --ppsc-ds-drawer-panel-duration: 0.28s;
  --ppsc-ds-drawer-panel-ease: cubic-bezier(0.32, 0.72, 0, 1);
  --ppsc-ds-backdrop-opacity: 0.6; /* no --opacity-60 */

  /* Stacking — no z-index scale */
  --ppsc-ds-z-header: 10;
  --ppsc-ds-z-skip-link: 11;

}

/* ============================================================
   FOCUS RING
   One colour, used by every :focus-visible outline, so a dark
   surface can swap it for itself: the teal default is
   under the 3:1 WCAG 2.1 AA minimum (1.4.11) on the dark blue hero
   and topbar and on every alert bar variant. Those components
   redefine --ppsc-ds-focus-ring on their own root rule.
   ============================================================ */
:root {
  --ppsc-ds-focus-ring: var(--secondary); /* 4.6:1 on white */
}

body {
  margin: 0;
}

/* ============================================================
   LAYOUT PRIMITIVES
   Shared with ppsc-ds.css, which documents the full section /
   container / stack model. Only the two the header markup actually
   uses live here:
   - .ppsc-ds-section    — the band. Owns the horizontal gutter.
   - .ppsc-ds-container  — caps content at the max content width.
   The surface modifiers, .ppsc-ds-section__stack and .ppsc-ds-page
   are page-level and stay in ppsc-ds.css.
   ============================================================ */
.ppsc-ds-section {
  width: 100%;
  box-sizing: border-box;
  padding-inline: clamp(1.125rem, 2.22vw + 0.631rem, var(--space-xl));
}

.ppsc-ds-container {
  width: 100%;
  max-width: var(--grid-viewport-max); /* 1332px */
  margin-inline: auto;
}

/* ============================================================
   ATOMS — Button
   Here rather than in ppsc-ds.css because the header needs it: the
   drawer Apply button, the mega menu band CTA and the alert bar CTA
   are all .ppsc-ds-btn. The whole family lives here, not only the
   variants the chrome uses.
   ============================================================ */

.ppsc-ds-btn {
  width: fit-content;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0;

  padding: var(--space-xs) var(--space-s);
  border-radius: var(--radius-s);

  font-family: var(--font-family);
  font-weight: var(--font-bold);
  font-size: var(--step-0);
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;

  transition:
    gap 0.2s ease,
    background-color 0.2s ease,
    color 0.2s ease,
    border-color 0.2s ease,
    opacity 0.2s ease;
}

/* Figma button anatomy, same for every type:
     space-s | space-2xs  label  space-2xs | 24px arrow box | space-s
   The label carries the space-2xs on both sides (Figma "Container"),
   the arrow box sits right after it with no gap, and the button hugs
   its content (no min-width). */
.ppsc-ds-btn__label {
  padding-inline: var(--space-2xs);
}

.ppsc-ds-btn__arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  transition: color 0.2s ease;
}
/* Figma chevron vector: 6.016 x 10.616 inside the 24px box. The SVG's
   7x12 viewBox draws the chevron at exactly that size at 1:1, so the
   SVG is sized to the viewBox, not to the chevron. */
.ppsc-ds-btn__arrow svg {
  width: 7px;
  height: 12px;
}

/* Back variant — mirrors the arrow to point left */
.ppsc-ds-btn--back .ppsc-ds-btn__arrow {
  transform: scaleX(-1);
}

/* Primary */
.ppsc-ds-btn--primary {
  background: var(--accent);
  color: var(--white);
  border: none;
}
.ppsc-ds-btn--primary:hover {
  background: var(--btn-primary-bg-hover);
  color: var(--white);
}
.ppsc-ds-btn--primary .ppsc-ds-btn__arrow {
  color: var(--white);
}

/* Secondary — gradient border via mask-composite ring */
.ppsc-ds-btn--secondary {
  position: relative;
  background: transparent;
  border: var(--border-m) solid transparent;
  isolation: isolate;
}
.ppsc-ds-btn--secondary::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  padding: var(--border-m);
  background: var(--gradient-button);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.ppsc-ds-btn--secondary::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--secondary);
  opacity: 0;
  transition: opacity 0.2s ease;
  z-index: -1;
}
.ppsc-ds-btn--secondary:hover {
  color: var(--white);
  border-color: transparent;
}
.ppsc-ds-btn--secondary:hover::after {
  opacity: 1;
}
.ppsc-ds-btn--secondary .ppsc-ds-btn__arrow {
  color: var(--tertiary);
}
.ppsc-ds-btn--secondary:hover .ppsc-ds-btn__arrow {
  color: var(--white);
}

.ppsc-ds-btn--secondary-dark {
  color: var(--neutral);
}
.ppsc-ds-btn--secondary-light {
  color: var(--white);
}
.ppsc-ds-btn--secondary-white {
  color: var(--white);
}
.ppsc-ds-btn--secondary-black {
  color: var(--neutral-ultra-dark);
}

/* Tertiary
   Keeps the shared button padding (space-xs / space-s) — the Figma
   inspector shows space-s inline on the CTA Banner and the Accordion
   actions row. Only min-width is dropped, since there is no fill to
   give a minimum width to.

   Where a tertiary sits flush against the content edge — the Icon Grid
   CTA and the Cards Cluster links — the padding comes off with
   --tertiary-no-padding below, on the button itself rather than
   reached through the parent. */
.ppsc-ds-btn--tertiary {
  background: transparent;
  border: none;
  min-width: 0;
}

/* The arrow only steps away from the label on tertiary. It moves with
   a transform, not by growing the gap, so the button keeps its width on
   hover and nothing beside it shifts. The 4px travel fits inside the
   arrow's 24px box. */
.ppsc-ds-btn--tertiary .ppsc-ds-btn__arrow {
  transition:
    color 0.2s ease,
    transform 0.2s ease;
}
.ppsc-ds-btn--tertiary:hover .ppsc-ds-btn__arrow {
  transform: translateX(calc(var(--space-2xs) - var(--space-3xs)));
}

/* Size S (Primary / Secondary): space-2xs all round, step--1 label,
   16px arrow box. Used by the CTA banner's Bottom fixed bar. */
.ppsc-ds-btn--s {
  padding: var(--space-2xs);
  font-size: var(--step--1);
}
.ppsc-ds-btn--s .ppsc-ds-btn__arrow {
  width: 16px;
  height: 16px;
}
.ppsc-ds-btn--s .ppsc-ds-btn__arrow svg {
  width: 4.6px; /* Figma Size S chevron is 4 x 7: the 7x12 viewBox at 0.66 */
  height: 7.9px;
}

/* Underline. Figma: 5% of the font size thick and 30% of it below the
   baseline, coloured with Figma's Buttons/Underline tokens: On-Light
   (Text/Muted) on the dark-text variants, On-Dark (Text/Inverse-Muted)
   on the light ones. On the label, so the arrow and the label's side
   padding stay clear. */
.ppsc-ds-btn--tertiary .ppsc-ds-btn__label {
  text-decoration: underline;
  text-decoration-thickness: 0.05em;
  text-underline-offset: 0.3em;
  text-underline-position: from-font;
  text-decoration-color: var(--btn-underline-on-light);
}
.ppsc-ds-btn--tertiary-light .ppsc-ds-btn__label,
.ppsc-ds-btn--tertiary-white .ppsc-ds-btn__label {
  text-decoration-color: var(--btn-underline-on-dark);
}

/* Tertiary has two independent axes in Figma:
     With padding (default) | No padding  — sets the spacing and arrow
     M (default)            | S           — sets the label size only
   so --tertiary-no-padding and --tertiary-s combine freely. */

/* No padding: the label's first letter lines up with the content edge
   (e.g. inside a card), so the button and the label both drop their
   padding. Figma: space-3xs between label and arrow, 16px arrow box
   with the Size S chevron, at both M and S. */
.ppsc-ds-btn--tertiary-no-padding {
  padding: 0;
  gap: var(--space-3xs);
}
.ppsc-ds-btn--tertiary-no-padding .ppsc-ds-btn__label {
  padding-inline: 0;
}
.ppsc-ds-btn--tertiary-no-padding .ppsc-ds-btn__arrow {
  width: 16px;
  height: 16px;
}
.ppsc-ds-btn--tertiary-no-padding .ppsc-ds-btn__arrow svg {
  width: 4.6px; /* same Size S chevron as .ppsc-ds-btn--s */
  height: 7.9px;
}

/* Size S: step--1 label. Padding and arrow come from the padding axis
   above, so S keeps the default padding unless --tertiary-no-padding is
   also set. */
.ppsc-ds-btn--tertiary-s {
  font-size: var(--step--1);
}
.ppsc-ds-btn--tertiary-dark {
  color: var(--text-default);
}
.ppsc-ds-btn--tertiary-dark:hover {
  color: var(--text-default);
}
.ppsc-ds-btn--tertiary-light {
  color: var(--white);
}
.ppsc-ds-btn--tertiary-light:hover {
  color: var(--white);
}
.ppsc-ds-btn--tertiary-white {
  color: var(--white);
}
.ppsc-ds-btn--tertiary-white:hover {
  color: var(--white);
}
.ppsc-ds-btn--tertiary-black {
  color: var(--neutral);
}
.ppsc-ds-btn--tertiary-black:hover {
  color: var(--neutral);
}

.ppsc-ds-btn--tertiary .ppsc-ds-btn__arrow {
  color: var(--neutral);
}
.ppsc-ds-btn--tertiary-dark .ppsc-ds-btn__arrow {
  color: var(--text-default);
}
.ppsc-ds-btn--tertiary-light .ppsc-ds-btn__arrow,
.ppsc-ds-btn--tertiary-white .ppsc-ds-btn__arrow {
  color: var(--white);
}

.ppsc-ds-btn:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  /* These set their transition LATER in the file, which would beat a
     plain `none` here at equal specificity — hence !important, the same
     deliberate exception as the header group below. Only applies when
     the visitor has reduce motion turned on. */
  .ppsc-ds-btn,
  .ppsc-ds-btn__arrow,
  .ppsc-ds-btn--secondary::after,
  .ppsc-ds-alert-bar {
    transition: none !important;
  }

  /* Header motion is declared later in the file (ORGANISMS and the
     RESPONSIVE > NAV block), so at equal specificity it would beat a
     plain `none` here. !important is the deliberate exception: a
     reduced-motion preference must win wherever the motion is set.
     ppsc-ds.js checks the same query and skips its animationend steps. */
  .ppsc-ds-header__nav-link,
  .ppsc-ds-header__nav-chevron,
  .ppsc-ds-header__nav,
  .ppsc-ds-header__nav-list,
  .ppsc-ds-header__backdrop,
  .ppsc-ds-header__search,
  .ppsc-ds-mega-menu,
  .ppsc-ds-mega-menu__link {
    transition: none !important;
    animation: none !important;
  }
}

/* ============================================================
   ORGANISMS — Header, mega menu, alert bar
   ============================================================ */

/* --- Legacy button reset ---
   The legacy stylesheet sets text-transform: uppercase and
   letter-spacing on bare button, and none of the rules below declare
   either, so on legacy pages every header <button> would inherit them.
   Listed by class rather than as `.ppsc-ds-header button`: one class
   beats the bare element selector whatever order the stylesheets load
   in, and still sits at the same weight as each component's own rule,
   so a component that ever needs caps can set it without a fight.
   .ppsc-ds-btn is here too because it can be a <button> on any page. */
.ppsc-ds-btn,
.ppsc-ds-header__utility,
.ppsc-ds-header__search-submit,
.ppsc-ds-header__search-close,
.ppsc-ds-header__menu-toggle,
.ppsc-ds-header__drawer-close,
.ppsc-ds-header__nav-link,
.ppsc-ds-mega-menu__back,
.ppsc-ds-alert-bar__close {
  text-transform: none;
  letter-spacing: normal;
}

/* --- Header ---
   Site chrome: skip link, topbar (utilities + Apply), navbar (logo +
   main nav), the mega menu panels, and — at NAV (≤1279.98px) — the
   drawer. Lives OUTSIDE <main>, so .ppsc-ds-page's overflow clip never
   reaches it.

   Third documented exception to the naked-organism model, same reason
   as Hero and Alert Bar: the dark topbar and the white bordered navbar
   are the header's identity, not a placement choice, so the header
   owns its surfaces. Horizontal gutter + max-width still come from
   .ppsc-ds-section / .ppsc-ds-container in the markup.

   Behaviour (ppsc-ds.js) writes state on the elements themselves:
   aria-expanded on each trigger, the hidden property on each panel,
   data-sibling-open on the other nav items, data-open on the nav and
   the hidden property on the backdrop. */

.ppsc-ds-header {
  position: relative; /* containing block for the desktop panels */
  z-index: var(--ppsc-ds-z-header);
  font-family: var(--font-family);
}

/* Off-screen until focused, then pinned top-left above the topbar. */
.ppsc-ds-header__skip-link {
  position: absolute;
  top: var(--space-2xs);
  left: var(--space-2xs);
  z-index: var(--ppsc-ds-z-skip-link);
  padding: var(--space-2xs) var(--space-s);
  border-radius: var(--radius-s);
  background: var(--surface-default);
  color: var(--text-link);
  font-family: var(--font-family);
  font-weight: var(--font-bold);
  font-size: var(--step--1);
  line-height: 1.2;
  text-decoration: underline;
  transform: translateY(calc(-100% - var(--space-s)));
}
.ppsc-ds-header__skip-link:focus {
  transform: none;
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}

.ppsc-ds-header__visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Topbar — Figma "Topbar / 2 /". The three utilities sit edge to edge
   (their own space-xs padding is the gap) and Apply steps off by one
   more space-xs. */
/* position + z-index keep the topbar and navbar (and so the open mega
   panel, which hangs off the navbar) above the page backdrop. Reset in
   RESPONSIVE > NAV, where the backdrop should dim the bars too. */
.ppsc-ds-header__topbar {
  --ppsc-ds-focus-ring: var(--white); /* 11:1 on --surface-inverse */
  position: relative;
  z-index: 2;
  background: var(--surface-inverse);
  color: var(--text-inverse);
}

.ppsc-ds-header__topbar-container {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

/* Search is a <button> (it discloses the search bar), Español and
   Student Login are <a>. The button reset below is what lets the three
   look identical — without it the UA gives Search a grey fill and an
   outset border. */
.ppsc-ds-header__utility {
  display: inline-flex;
  align-items: center;
  padding: var(--space-xs);
  border: 0;
  border-radius: var(--radius-s);
  background: none;
  cursor: pointer;
  appearance: none;
  color: var(--text-inverse);
  font-family: var(--font-family);
  font-weight: var(--font-bold);
  font-size: var(--step--1);
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
}
.ppsc-ds-header__utility:hover {
  color: var(--text-inverse);
  text-decoration: underline;
  text-underline-position: from-font;
}
.ppsc-ds-header__utility:focus-visible {
  outline: 2px solid var(--border-inverse);
  outline-offset: -2px;
}

/* Figma's 20px icon slot holding a 24px glyph — the glyph overflows
   the slot by 2px each side, which is what sets the label's position. */
.ppsc-ds-header__utility-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
}
.ppsc-ds-header__utility-icon svg {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
}

.ppsc-ds-header__utility-label {
  padding-inline: var(--space-2xs);
}

/* Apply — Primary button, Size S. Double-class on .ppsc-ds-btn: same
   specificity, later in the file, so these win over the atom's
   Default-size padding, min-width and type size. */
.ppsc-ds-header__apply {
  min-width: 0;
  margin-left: var(--space-xs);
  padding: var(--space-2xs);
  gap: 0;
  font-size: var(--step--1);
}

.ppsc-ds-header__apply-label {
  padding-inline: var(--space-2xs);
}

.ppsc-ds-header__apply-arrow {
  display: flex;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  color: var(--white);
}
.ppsc-ds-header__apply-arrow svg {
  width: 100%;
  height: 100%;
}

/* --- Topbar search --- Figma "Nav > Search bar=Open" / "Text input".
   A real <form> that GETs the live PPSC search, so Enter submits and
   the control works with JavaScript disabled once it is open.

   Layout: the form is the FIRST child of the topbar container, which
   is justify-content:flex-end. Closed it is `hidden`, so it takes no
   space and the utilities sit right; open it is a flex item
   with flex:1, so it fills everything left of the utilities and pushes
   nothing around. That is Figma's `flex: 1 0 0` on the text input.

   Open/closed is the `hidden` property plus data-open, NOT visibility:
   a search bar that still occupied its row while closed would hold the
   utilities out of position. hidden also keeps the input out of the
   tab order and the accessibility tree while closed. The fade needs
   the element rendered, so ppsc-ds.js removes hidden first and sets
   data-open on the next frame; closing reverses it and waits for
   transitionend before setting hidden again (reduced motion skips the
   wait — see the header group in the reduced-motion block above). */
/* The slot is the flex item that claims the free space between the
   logo edge and the utilities. The bar fills it absolutely and the
   trigger sits at its right edge — which is exactly where the trigger
   sits when there is no bar, so nothing moves when one arrives. */
.ppsc-ds-header__search-slot {
  position: relative;
  display: flex;
  flex: 1 0 0;
  justify-content: flex-end;
  min-width: 0;
}

.ppsc-ds-header__search {
  /* Absolute so it takes no room from the trigger beside it. Centred
     on its own height rather than stretched, because the desktop bar
     is the 34px pill from Figma inside a taller topbar. */
  position: absolute;
  inset-inline: 0;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: var(--ppsc-ds-header-search-gap);
  min-width: 0; /* a flex item's auto min-width would floor it at the
                   input's default size and overflow the topbar */
  padding: var(--space-3xs) var(--space-xs);
  border-radius: var(--radius-s);
  background: var(--surface-default);
  opacity: 0;
  transition: opacity var(--ppsc-ds-header-search-duration) ease;
}
.ppsc-ds-header__search[data-open="true"] {
  opacity: 1;
}

/* The bar sits on white, inside a topbar that redefined the focus ring
   to white for the dark background. Put it back to the default or the
   ring is invisible against the field. */
.ppsc-ds-header__search {
  --ppsc-ds-focus-ring: var(--secondary);
}

/* Focusing the field rings the whole bar, not the input inside it.
   The input has no border of its own — the white bar is the field as
   far as anyone looking at it is concerned — so an outline around
   just the input draws a box inside a box, which is not in the design
   and reads as a mistake.

   :has() picks out the field specifically, so a focused button shows
   its own ring instead of both. Without :has(), the plain
   :focus-within fallback rings the bar for anything focused inside:
   redundant next to a button's own ring, but visible, which is the
   right way round for a focus indicator to fail.

   :focus and not :focus-visible inside the :has(). The bar moves
   focus into the field as it opens, so the ring is showing whenever
   the bar is open, which is simply what a focused field looks like.
   :focus-visible would leave the whole thing resting on a heuristic,
   and if it ever failed to match there would be no ring at all, since
   the fallback above is switched off in here. */
.ppsc-ds-header__search:focus-within {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}
@supports selector(:has(*)) {
  .ppsc-ds-header__search:focus-within {
    outline: none;
  }
  .ppsc-ds-header__search:has(.ppsc-ds-header__search-input:focus) {
    outline: 2px solid var(--ppsc-ds-focus-ring);
    outline-offset: 2px;
  }
}

/* The magnifier submits. It is a real submit button rather than a
   decorative glyph, so clicking it runs the search, with the UA's own
   button chrome reset off and a visually hidden name. Enter in the
   field submits the same form. */
.ppsc-ds-header__search-submit {
  display: flex;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-default);
  cursor: pointer;
  appearance: none;
}
.ppsc-ds-header__search-submit svg {
  width: 100%;
  height: 100%;
}
.ppsc-ds-header__search-submit:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}

/* type="search" so mobile keyboards offer a search key; the UA's own
   clear widget is removed because the design has an explicit close. */
.ppsc-ds-header__search-input {
  flex: 1 0 0;
  min-width: 0;
  border: 0;
  background: none;
  color: var(--text-default);
  font-family: var(--font-family);
  font-weight: var(--font-regular);
  font-size: var(--step--1);
  line-height: 1.2;
  appearance: none;
}
.ppsc-ds-header__search-input::placeholder {
  color: var(--text-muted);
  opacity: 1; /* Firefox dims placeholders by default */
}
.ppsc-ds-header__search-input::-webkit-search-cancel-button,
.ppsc-ds-header__search-input::-webkit-search-decoration {
  appearance: none;
}
/* No ring of its own: the bar carries it (see above). */
.ppsc-ds-header__search-input:focus-visible {
  outline: none;
}

.ppsc-ds-header__search-close {
  display: flex;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-default);
  cursor: pointer;
}
.ppsc-ds-header__search-close-icon {
  width: 100%;
  height: 100%;
}
/* Desktop shows the 24px framed glyph; NAV swaps to the 18px one. */
.ppsc-ds-header__search-close-icon--s {
  display: none;
}
.ppsc-ds-header__search-close:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}

/* While the bar is open the trigger is redundant, so it fades out
   underneath it — it does NOT leave the row, so the two cross-fade in
   the same place.

   The declaration lives only in the open state on purpose: transition
   is a shorthand, and writing one at rest would clobber whatever the
   element animates for its own hover. visibility is delayed by the
   fade so the trigger stays readable underneath while the bar
   arrives, and snaps back the moment the close starts. */
.ppsc-ds-header__topbar-container[data-search-open="true"]
  .ppsc-ds-header__utility--search {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--ppsc-ds-header-search-duration) ease,
    visibility 0s linear var(--ppsc-ds-header-search-duration);
}

/* Navbar — Figma "Navbar / 1 /". 72px tall including its bottom
   border (Figma strokes inside), which is space-2xl. */
.ppsc-ds-header__navbar {
  position: relative;
  z-index: 2;
  background: var(--surface-default);
  border-bottom: var(--border-width-default) solid var(--border-default);
}

.ppsc-ds-header__navbar-container {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  min-height: calc(var(--space-2xl) - var(--border-width-default));
}

.ppsc-ds-header__logo {
  display: flex;
  align-self: center;
  flex-shrink: 0;
  width: var(--ppsc-ds-logo-width);
  height: var(--ppsc-ds-logo-height);
}
.ppsc-ds-header__logo svg {
  width: 100%;
  height: 100%;
}
.ppsc-ds-header__logo:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 4px;
}

/* Menu / Close — drawer triggers, NAV breakpoint only. */
.ppsc-ds-header__menu-toggle,
.ppsc-ds-header__drawer-close {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  align-self: center;
  flex-shrink: 0;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--text-default);
  cursor: pointer;
}
.ppsc-ds-header__menu-toggle:focus-visible,
.ppsc-ds-header__drawer-close:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}

.ppsc-ds-header__menu-toggle-icon {
  display: flex;
  width: 32px;
  height: 32px;
}
.ppsc-ds-header__menu-toggle-icon svg {
  width: 100%;
  height: 100%;
}

.ppsc-ds-header__menu-toggle-label {
  font-family: var(--font-family);
  font-weight: var(--font-regular);
  font-size: var(--step--2);
  line-height: 1.2;
}

.ppsc-ds-header__nav {
  display: flex;
}

/* Drawer-only chrome: hidden on desktop, shown in RESPONSIVE > NAV. */
.ppsc-ds-header__drawer-head,
.ppsc-ds-header__drawer-footer {
  display: none;
}

/* Explicit list reset — Bootstrap's reboot gives ul a padding-left
   and margin-bottom that the universal reset can't out-rank. */
.ppsc-ds-header__nav-list {
  display: flex;
  align-items: stretch;
  /* No gap between items: a gap here is dead space that the
     hover fill cannot cover, so moving along the bar the highlight
     blinks off between one item and the next. The links carry their own
     inline padding, so they still read as separate without it. */
  margin: 0;
  padding: 0;
  list-style: none;
}

.ppsc-ds-header__nav-item {
  display: flex;
}

/* Figma "Nav link": space-2xs outer inline padding and space-3xs around
   the label. Full navbar height so the Open fill runs top to bottom.

   No flex gap: the label's own padding is the whole separation between
   the label and its chevron. */
.ppsc-ds-header__nav-link {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0 var(--space-2xs);
  background: transparent;
  border: 0;
  color: var(--text-default);
  font-family: var(--font-family);
  font-weight: var(--font-semibold);
  font-size: var(--step--1);
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color 0.2s ease,
    color 0.2s ease;
}
/* Hover takes the Open fill, so moving across the bar previews exactly
   what a click will show. */
.ppsc-ds-header__nav-link:hover,
.ppsc-ds-header__nav-link[aria-expanded="true"] {
  background: var(--btn-secondary-outline-hover);
  color: var(--text-inverse);
}
.ppsc-ds-header__nav-link:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: -2px;
}

.ppsc-ds-header__nav-label {
  padding-inline: var(--space-3xs);
}

.ppsc-ds-header__nav-chevron,
.ppsc-ds-header__nav-arrow {
  display: flex;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
}
.ppsc-ds-header__nav-chevron svg,
.ppsc-ds-header__nav-arrow svg {
  width: 100%;
  height: 100%;
}

/* Figma's Open chevron points up; same glyph, turned. */
.ppsc-ds-header__nav-chevron {
  transition: transform 0.2s ease;
}
.ppsc-ds-header__nav-link[aria-expanded="true"]
  .ppsc-ds-header__nav-chevron {
  transform: rotate(180deg);
}

/* Right-pointing arrow is the drawer affordance only. Figma exports the
   left-pointing glyph and turns the instance 180deg; same here. */
.ppsc-ds-header__nav-arrow {
  display: none;
  transform: rotate(180deg);
}

/* Written on <html> itself by ppsc-ds.js while the drawer is open. */
html[data-ppsc-ds-scroll-lock="true"] {
  overflow: hidden;
}

/* Drawer sub-panel slides (used in RESPONSIVE > NAV only). Keyframes
   can't live inside the NAV @media block's rules, so they sit here. */
@keyframes ppsc-ds-drawer-panel-in {
  from {
    transform: translateX(100%);
  }
  to {
    transform: none;
  }
}
@keyframes ppsc-ds-drawer-panel-out {
  from {
    transform: none;
  }
  to {
    transform: translateX(100%);
  }
}
@keyframes ppsc-ds-drawer-list-in {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: none;
  }
}

/* Page backdrop behind an open mega menu (desktop) or the drawer.
   Figma "Mobile open": black at 60%.

   Always rendered, always on its own compositing layer, and never
   visibility:hidden — for the same reason as the drawer. This is a
   full-viewport element whose opacity animates: with no layer of its
   own, WebKit has to re-blend everything underneath it on every frame
   of the fade, and on a long content-heavy page that is expensive. The
   backdrop is a flat colour, so the layer itself is cheap.

   pointer-events replaces visibility for keeping a closed backdrop from
   swallowing clicks; it has no content, so there is nothing for it to
   contribute to the accessibility tree either way. The fade matches the
   mega panels here and the drawer slide in RESPONSIVE > NAV. */
.ppsc-ds-header__backdrop {
  position: fixed;
  inset: 0;
  z-index: 1;
  background: var(--black);
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
  transition: opacity 0.2s ease;
}
.ppsc-ds-header__backdrop[data-open="true"] {
  opacity: var(--ppsc-ds-backdrop-opacity);
  pointer-events: auto;
}

/* Drawer footer — Student Login / Español (Tertiary M, left icon) and
   a full-width Apply (Primary M). */
.ppsc-ds-header__drawer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

/* Content-width basis, so the pair only wraps to one per row when the
   two genuinely stop fitting side by side (Figma "Narrow viewport"),
   with no extra breakpoint; side by side they share the spare width.
   No right-hand padding on the link or its label: it is invisible (no
   fill) and would only make the pair wrap earlier. Icon and label
   keep Figma's left insets. */
.ppsc-ds-header__drawer-link {
  flex: 1 1 auto;
  justify-content: flex-start;
  gap: 0;
  padding: var(--space-xs) 0 var(--space-xs) var(--space-xs);
}
.ppsc-ds-header__drawer-link:hover {
  gap: 0;
}

.ppsc-ds-header__drawer-link-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
}
.ppsc-ds-header__drawer-link-icon svg {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
}

/* Own label class, so it takes the tertiary underline here. */
.ppsc-ds-header__drawer-link-label {
  padding-left: var(--space-2xs);
  text-decoration: underline;
  text-decoration-thickness: 0.05em;
  text-underline-offset: 0.3em;
  text-underline-position: from-font;
  text-decoration-color: var(--btn-underline-on-light);
}

.ppsc-ds-header__drawer-apply {
  width: 100%;
  min-width: 0;
  padding: var(--space-xs);
  gap: 0;
  font-size: var(--step--1);
}

/* --- Mega menu ---
   One panel per main nav item, always in the DOM, shown by data-open
   (written by ppsc-ds.js). Desktop: a full-width sheet hung off the
   bottom of .ppsc-ds-header. Drawer: the same markup flows inline and
   replaces the nav list (see RESPONSIVE > NAV).

   Visibility is data-open + visibility, NOT the hidden property: every
   panel shares the same absolute slot, so switching items cross-fades
   the outgoing panel over the incoming one instead of swapping them in
   one frame, and display:none would give that nothing to animate.
   visibility:hidden still takes a closed panel out of the tab order and
   the accessibility tree. It flips at the END of a close (delayed) and
   at the START of an open (no delay).

   Layout: optional feature card (fixed 416px column) + a grid of link
   groups whose column count is a custom property, like Icon Grid and
   Cards Cluster. Optional accent band underneath. */

.ppsc-ds-mega-menu {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  overflow: hidden;
  background: var(--surface-default);
  border-radius: 0 0 var(--ppsc-ds-mega-menu-radius)
    var(--ppsc-ds-mega-menu-radius);
  visibility: hidden;
  opacity: 0;
  transform: translateY(calc(-1 * var(--space-2xs)));
  transition:
    opacity 0.2s ease,
    transform 0.2s ease,
    visibility 0s linear 0.2s;
}

/* The incoming panel stacks above the one fading out. */
.ppsc-ds-mega-menu[data-open="true"] {
  z-index: 1;
  visibility: visible;
  opacity: 1;
  transform: none;
  transition:
    opacity 0.2s ease,
    transform 0.2s ease,
    visibility 0s;
}

/* Drawer-only "Go back" row. */
.ppsc-ds-mega-menu__back-row {
  display: none;
}

/* Body and band repeat the .ppsc-ds-section gutter rather than carrying
   the class: MOBILE redefines .ppsc-ds-section after the NAV block, and
   would beat the drawer's fixed space-s gutter. Keep in step with
   .ppsc-ds-section. */
.ppsc-ds-mega-menu__body {
  padding-block: var(--space-xl);
  padding-inline: clamp(1.125rem, 2.22vw + 0.631rem, var(--space-xl));
}

.ppsc-ds-mega-menu__container {
  display: flex;
  align-items: stretch;
  gap: var(--space-2xl);
}

/* Feature card — photo, dark top-down scrim, inverse text. The scrim is
   --primary-ultra-dark at 90% fading to clear by half height; opacity
   on a pseudo-element keeps it on tokens without color-mix(). */
.ppsc-ds-mega-menu__feature {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  flex: 0 0 var(--ppsc-ds-mega-menu-feature-width);
  padding: var(--space-l);
  overflow: hidden;
  border-radius: var(--radius-l);
  color: var(--text-inverse);
  text-decoration: none;
}
.ppsc-ds-mega-menu__feature::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    var(--primary-ultra-dark),
    transparent 50%
  );
  opacity: var(--opacity-90);
  pointer-events: none;
}
.ppsc-ds-mega-menu__feature:hover {
  color: var(--text-inverse);
}
.ppsc-ds-mega-menu__feature:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}
/* A feature card without a photo gets a solid dark background so its
   white text stays readable. */
.ppsc-ds-mega-menu__feature--no-image {
  background: var(--primary-ultra-dark);
}

.ppsc-ds-mega-menu__feature-image {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ppsc-ds-mega-menu__feature-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

/* The CMS builds the card as a <div class="__feature"> with the link on
   the content instead (<a class="__feature-content">). Same look: the
   link takes the card's colour without an underline, and its ::before
   stretches over the whole card (the div is position: relative) so the
   photo is still clickable. The focus ring moves to the card. */
a.ppsc-ds-mega-menu__feature-content {
  color: inherit;
  text-decoration: none;
}
a.ppsc-ds-mega-menu__feature-content::before {
  content: "";
  position: absolute;
  inset: 0;
}
a.ppsc-ds-mega-menu__feature-content:focus-visible {
  outline: none;
}
.ppsc-ds-mega-menu__feature:has(
    > a.ppsc-ds-mega-menu__feature-content:focus-visible
  ) {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}

.ppsc-ds-mega-menu__feature-heading {
  font-weight: var(--font-bold);
  font-size: var(--step-3);
  line-height: 1.2;
}

.ppsc-ds-mega-menu__feature-text {
  color: var(--text-inverse);
  font-weight: var(--font-regular);
  font-size: var(--step-0);
  line-height: 1.2;
}

.ppsc-ds-mega-menu__groups {
  --ppsc-ds-mega-menu-cols: 2;
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(var(--ppsc-ds-mega-menu-cols), minmax(0, 1fr));
  align-content: start;
  column-gap: var(--space-l);
  row-gap: var(--space-xs);
}

.ppsc-ds-mega-menu__groups--cols-3 {
  --ppsc-ds-mega-menu-cols: 3;
}

/* Explore content is authored in two source columns: primary groups in
   column 1, then secondary groups in column 2. Flatten the headings and
   lists into a two-column grid so those source columns become two rows. */
.ppsc-ds-mega-menu__groups--explore {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-flow: row;
}

.ppsc-ds-mega-menu__group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  min-width: 0;
}

/* Figma "Others/Taglines M": all-small-caps, not an uppercase
   transform — the variable font draws real small caps. */
.ppsc-ds-mega-menu__group-heading {
  margin: 0;
  padding: var(--ppsc-ds-tagline-padding-block)
    var(--ppsc-ds-tagline-padding-inline);
  border-bottom: var(--border-width-default) solid var(--border-default);
  color: var(--text-muted);
  font-family: var(--font-family);
  font-weight: var(--font-semibold);
  font-size: var(--step--1);
  font-variant: var(--ppsc-ds-font-variant-small-caps);
  line-height: 1.2;
  letter-spacing: var(--ppsc-ds-tracking-tagline);
}

.ppsc-ds-mega-menu__list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Explore lists (Figma "↳ Nav" > Explore). The extra inset sits on the
   list, not on the link, so a link's hover fill keeps its own single
   space-xs and starts where the wrapper does.
   --icons: icon + description links, space-xs apart.
   --nested: plain links under "Paying for college", one space-xs in.
   --inset: the lower row, a space-2xs / space-xs wrapper. */
.ppsc-ds-mega-menu__list--icons {
  gap: var(--space-xs);
}
.ppsc-ds-mega-menu__list--nested {
  padding-inline: var(--space-xs);
}
.ppsc-ds-mega-menu__list--inset {
  padding: var(--space-2xs) var(--space-xs);
}

/* Bootstrap's reboot gives hr a 1px currentColor fill at 25% opacity;
   the rule here is the border alone. */
.ppsc-ds-mega-menu__divider {
  grid-column: 1 / -1;
  height: 0;
  margin: 0;
  border: 0;
  border-top: var(--border-width-default) solid var(--border-default);
  background: none;
  opacity: 1;
}

/* Figma hover: neutral fill, radius-m, underlined label. */
.ppsc-ds-mega-menu__link {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  padding: var(--space-2xs) var(--space-xs);
  border-radius: var(--radius-m);
  text-decoration: none;
  transition: background-color 0.2s ease;
}
.ppsc-ds-mega-menu__link:hover {
  background: var(--surface-neutral);
}
.ppsc-ds-mega-menu__link:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: -2px;
}

.ppsc-ds-mega-menu__link--icon {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--space-2xs);
}

.ppsc-ds-mega-menu__link-icon {
  display: flex;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
}
.ppsc-ds-mega-menu__link-icon svg {
  width: 100%;
  height: 100%;
}

.ppsc-ds-mega-menu__link-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-3xs);
  min-width: 0;
}

.ppsc-ds-mega-menu__link-label {
  color: var(--text-link);
  font-family: var(--font-family);
  font-weight: var(--font-semibold);
  font-size: var(--step-0);
  line-height: 1.2;
}
.ppsc-ds-mega-menu__link:hover .ppsc-ds-mega-menu__link-label {
  text-decoration: underline;
  text-underline-position: from-font;
}

.ppsc-ds-mega-menu__link-description {
  color: var(--text-default);
  font-family: var(--font-family);
  font-weight: var(--font-regular);
  font-size: var(--step--1);
  line-height: 1.2;
}

/* Accent band — message + Tertiary M. */
.ppsc-ds-mega-menu__band {
  padding-block: var(--space-s);
  padding-inline: clamp(1.125rem, 2.22vw + 0.631rem, var(--space-xl));
  background: var(--surface-accent);
}

.ppsc-ds-mega-menu__band-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
}

.ppsc-ds-mega-menu__band-text {
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--space-2xs);
  margin: 0;
  color: var(--text-heading);
  font-family: var(--font-family);
  font-weight: var(--font-semibold);
  font-size: var(--step-0);
  line-height: 1.2;
}

.ppsc-ds-mega-menu__band-body {
  font-weight: var(--font-regular);
}

.ppsc-ds-mega-menu__band-cta {
  flex-shrink: 0;
}

/* --- Alert bar ---
   Horizontal gutter + max-width come from .ppsc-ds-section /
   .ppsc-ds-container (applied in markup). This class only owns
   background + vertical padding; the container child owns the
   internal flex layout. */

.ppsc-ds-alert-bar {
  display: grid;
  grid-template-rows: 1fr;
  padding-block: var(--space-2xs);
  overflow: hidden;
  transition:
    grid-template-rows 0.3s ease,
    padding-block 0.3s ease,
    opacity 0.25s ease,
    transform 0.3s ease;
}

.ppsc-ds-alert-bar--closing {
  grid-template-rows: 0fr;
  padding-block: 0;
  opacity: 0;
  transform: translateY(-100%);
}

/* Three children: icon, content, close. Figma puts space-xs
   between the icon and the text (space-2xs on mobile) and space-s
   before the close, so the gap carries the icon pair and the close
   adds the difference. Both halves step down in RESPONSIVE. */
.ppsc-ds-alert-bar__container {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-xs);
  min-height: 0;
  overflow: hidden;
}

/* Figma: a 24px library icon (bell / info / warning by variant),
   top-aligned with the text. Colour is inherited so the Urgent
   variant's white text takes the icon with it, rather than pinning it
   to the on-light fill. */
.ppsc-ds-alert-bar__icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  color: inherit;
}
.ppsc-ds-alert-bar__icon svg {
  display: block;
}

.ppsc-ds-alert-bar--info {
  --ppsc-ds-focus-ring: var(--text-default); /* matches the bar's text */
  background: var(--surface-highlight);
  color: var(--text-default);
}
.ppsc-ds-alert-bar--warning {
  --ppsc-ds-focus-ring: var(--text-default);
  background: var(--surface-warning);
  color: var(--text-default);
}
.ppsc-ds-alert-bar--urgent {
  --ppsc-ds-focus-ring: var(--white);
  background: var(--surface-danger);
  color: var(--white);
}

.ppsc-ds-alert-bar__content {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.ppsc-ds-alert-bar__text {
  margin: 0;
  font-family: var(--font-family);
  font-weight: var(--font-regular);
  font-size: var(--step-0);
  line-height: 1.2; /* Default/Step 0 in Figma, 120% */
  color: inherit;
}

.ppsc-ds-alert-bar__close {
  flex-shrink: 0;
  /* Container gap is the icon-to-text value; this makes up the
     difference to the space-s Figma shows before the close. */
  margin-left: calc(var(--space-s) - var(--space-xs));
  width: 32px; /* Figma 32px icon, 18px cross */
  height: 32px;
  padding: 0;

  display: flex;
  align-items: center;
  justify-content: center;

  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
}

/* The button's own block padding would stack on top of __content's
   space-2xs gap, so the gap between the message and the link reads as
   23px rather than the 9px it should be. Zeroed on all four sides
   here; the alert bar's own padding-block is what spaces the strip. */
.ppsc-ds-alert-bar .ppsc-ds-btn {
  padding-top: 0;
  padding-bottom: 0;
  padding-left: 0;
}

.ppsc-ds-alert-bar .ppsc-ds-btn__label {
  padding-inline-start: 0;
}

.ppsc-ds-alert-bar .ppsc-ds-btn__arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  transition: color 0.2s ease;
}
.ppsc-ds-alert-bar .ppsc-ds-btn__arrow svg {
  width: 10px;
  height: 10px;
}

.ppsc-ds-alert-bar__close:focus-visible {
  outline: 2px solid var(--ppsc-ds-focus-ring);
  outline-offset: 2px;
}

/* ============================================================
   SITE LINK GUARD
   The live site's stylesheets colour every link by state:
     a:visited { color: var(--tropicana) }   a:hover { color: var(--navy) }
     a:active  { color: var(--tangerine-tango) }   (+ Bootstrap a:hover)
   At 0,1,1 those beat any single-class colour rule here (0,1,0), so a
   visited Español link turns orange and an unhovered-by-design button
   turns navy on hover.

   Each rule below repeats one single-class colour rule from above as
   a.<class> in those three states only. 0,1,1 ties the site's rules
   and wins by load order (this file loads after them); our own :hover
   rules (0,2,0) and context rules like .ppsc-ds-accordion--dark .x
   still win over it. Kept in the same order as the originals, so a
   modifier still beats its base class. Plain site links are not
   touched — only <a>s carrying these classes.
   ============================================================ */
a.ppsc-ds-btn--primary:where(:visited, :hover, :active) {
  color: var(--white);
}
a.ppsc-ds-btn--secondary-dark:where(:visited, :hover, :active) {
  color: var(--neutral);
}
a.ppsc-ds-btn--secondary-light:where(:visited, :hover, :active) {
  color: var(--white);
}
a.ppsc-ds-btn--secondary-white:where(:visited, :hover, :active) {
  color: var(--white);
}
a.ppsc-ds-btn--secondary-black:where(:visited, :hover, :active) {
  color: var(--neutral-ultra-dark);
}
a.ppsc-ds-btn--tertiary-dark:where(:visited, :hover, :active) {
  color: var(--text-default);
}
a.ppsc-ds-btn--tertiary-light:where(:visited, :hover, :active) {
  color: var(--white);
}
a.ppsc-ds-btn--tertiary-white:where(:visited, :hover, :active) {
  color: var(--white);
}
a.ppsc-ds-btn--tertiary-black:where(:visited, :hover, :active) {
  color: var(--neutral);
}
a.ppsc-ds-header__skip-link:where(:visited, :hover, :active) {
  color: var(--text-link);
}
a.ppsc-ds-header__utility:where(:visited, :hover, :active) {
  color: var(--text-inverse);
}
a.ppsc-ds-mega-menu__feature:where(:visited, :hover, :active),
a.ppsc-ds-mega-menu__feature-content:where(:visited, :hover, :active) {
  color: var(--text-inverse);
}

/* ============================================================
   RESPONSIVE
   Every @media override for the chrome lives here, grouped by
   breakpoint.

   Order matters: LOGO (<=1339.98px) first, then NAV (<=1279.98px),
   then MOBILE (<=767.98px) last. Each narrower range sits fully inside
   the one before it, so putting it later means it wins any property
   both blocks touch, which is the correct precedence.

   LOGO and NAV are header-only and content-driven, not page
   breakpoints: logo + seven nav links need ~1330px at full logo size.
   LOGO buys ~58px by shrinking the logo first; below NAV the bar would
   overflow, so the drawer takes over.

   ppsc-ds.css keeps its own TABLET (<=1023.98px) and MOBILE blocks for
   the page components. Because ppsc-ds.css loads AFTER this file, its
   MOBILE block wins any property both touch — nothing overlaps today.
   ============================================================ */

/* --- LOGO (≤1339.98px) — logo steps to its mobile size, nav stays --- */
@media (max-width: 1339.98px) {
  /* 260px (Figma mobile size) at most; below that it shrinks with its
     row, keeping the artwork's 260:18 ratio, once the Menu / Close
     button and the row gap no longer fit beside it. */
  .ppsc-ds-header__logo {
    flex-shrink: 1;
    width: var(--ppsc-ds-logo-width-s);
    min-width: 0;
    height: auto;
    aspect-ratio: 260 / 18;
  }
}

/* --- NAV (≤1279.98px) — header switches to Menu button + drawer --- */
@media (max-width: 1279.98px) {
  /* Topbar — Figma "Mobile by default": Search left, icon-only Login
     and Apply right, Español moves into the drawer footer. */
  /* Figma "Topbar / 2": space-3xs above and below, and the bar hugs to
     48px — 5 + the 38px Search trigger + 5. The open search field is
     also 38px, so the topbar is the same height in both states. */
  .ppsc-ds-header__topbar-container {
    gap: var(--space-xs);
    padding-block: var(--space-3xs);
  }
  .ppsc-ds-header__utility {
    gap: var(--space-3xs);
    padding-block: 0;
  }
  .ppsc-ds-header__utility-label {
    padding-inline: 0;
  }
  .ppsc-ds-header__utility-label--collapsible {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .ppsc-ds-header__utility--search {
    margin-right: auto;
  }
  .ppsc-ds-header__utility--language {
    display: none;
  }
  .ppsc-ds-header__apply {
    margin-left: 0;
  }

  /* Search open on a phone (Figma "Topbar / 2"): the bar spans the
     whole row. There is no room for a usable field beside Student
     Login and Apply at 375px.

     It OVERLAYS them rather than replacing them: absolutely positioned,
     the bar needs no room from anyone, so nothing moves and the two
     states cross-fade. */
  .ppsc-ds-header__topbar-container {
    position: relative;
  }
  /* The slot stops being the positioning context here, so the bar
     resolves against the container instead and can cover the whole
     row — Login and Apply included, which the desktop bar sits beside
     rather than over. */
  .ppsc-ds-header__search-slot {
    position: static;
  }
  /* Full width of the container (so the gutter is the topbar's own),
     38px tall, still centred by the base rule. At 38px it clears
     Apply, the tallest thing behind it, so nothing shows past its
     edges, and the dark band stays visible above and below it. */
  .ppsc-ds-header__search {
    height: var(--ppsc-ds-header-search-height-s);
  }

  /* Figma "Vector", mobile search bar: the close is 18px here, not the
     24px icon frame of the desktop bar, and its X fills the box rather
     than sitting inside padding. */
  .ppsc-ds-header__search-close {
    width: 18px;
    height: 18px;
  }
  .ppsc-ds-header__search-close-icon--l {
    display: none;
  }
  .ppsc-ds-header__search-close-icon--s {
    display: block;
  }

  /* Figma "Button / Theme=White / Size=M": the mobile Search trigger
     carries space-2xs above and below its label, which the general
     utility rule above zeroes for the icon-only ones. That makes the
     trigger 38px — the same height as the open search field, so the
     topbar does not change height between the two states.

     space-2xs for the icon-to-label gap too, so the token carries it
     rather than a literal in LOCAL VALUES. Scoped to Search because
     it is the only utility still showing a label at this width. */
  .ppsc-ds-header__utility--search {
    gap: var(--space-2xs);
    padding-block: var(--space-2xs);
  }

  /* The other half of the cross-fade: on a phone the bar covers Login
     and Apply too, so they fade with the trigger.

     Open state only, with no resting-state counterpart: transition is
     a shorthand, and a resting one would drop the hover transitions
     Apply has as a .ppsc-ds-btn. Closing snaps them back while the bar
     still covers them.

     visibility, not display: out of the tab order and the
     accessibility tree while covered, at no reflow cost. It is
     delayed by the fade so they stay visible underneath while the bar
     arrives — the same timing the mega menu panels use. */
  .ppsc-ds-header__topbar-container[data-search-open="true"]
    .ppsc-ds-header__utility,
  .ppsc-ds-header__topbar-container[data-search-open="true"]
    .ppsc-ds-header__apply {
    opacity: 0;
    visibility: hidden;
    transition:
      opacity var(--ppsc-ds-header-search-duration) ease,
      visibility 0s linear var(--ppsc-ds-header-search-duration);
  }

  .ppsc-ds-header__navbar-container {
    align-items: center;
    gap: var(--space-s);
    min-height: 0;
    padding-block: var(--space-xs);
  }
  .ppsc-ds-header__menu-toggle,
  .ppsc-ds-header__drawer-close {
    display: flex;
  }

  /* Drawer — pinned right, 36px of backdrop showing on the left at
     phone width, hugging its content up to the viewport height.

     Slides in from the right and back out. Closed, it sits fully off
     canvas. */
  .ppsc-ds-header__nav {
    position: fixed;
    top: 0;
    right: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    width: calc(100% - var(--space-l));
    max-width: var(--ppsc-ds-drawer-max-width);
    max-height: 100vh;
    max-height: 100dvh;
    overflow-y: auto;
    overflow-x: hidden; /* clips the sub-panel slides */
    overscroll-behavior: contain;
    background: var(--surface-default);
    border-bottom-left-radius: var(--radius-l);
    /* Never visibility:hidden at this breakpoint: WebKit keeps no layer
       for a hidden element, so the open would not animate. It stays
       visible and composited, parked off the right edge, and will-change
       keeps that layer alive — do not remove it.

       Off-canvas it must still be untouchable, untabbable and unseen by
       assistive tech: pointer-events here, and the inert property from
       ppsc-ds.js. Browsers without inert (iOS Safari below 15.5) get
       visibility:hidden via [data-inert-fallback]. */
    visibility: visible;
    transform: translateX(100%);
    pointer-events: none;
    will-change: transform;
    /* A plain CSS transition, triggered by data-open. */
    transition: transform var(--ppsc-ds-drawer-duration)
      var(--ppsc-ds-drawer-ease);
  }
  .ppsc-ds-header__nav[data-open="true"] {
    transform: none;
    pointer-events: auto;
  }
  .ppsc-ds-header__nav[data-inert-fallback="true"]:not([data-open="true"]) {
    visibility: hidden;
  }

  /* Backdrop fades in step with the drawer slide, and dims the bars as
     well (Figma "Mobile open"), so they drop back into the header's own
     stacking order. */
  .ppsc-ds-header__backdrop {
    transition: opacity var(--ppsc-ds-drawer-duration)
      var(--ppsc-ds-drawer-ease);
  }
  .ppsc-ds-header__topbar,
  .ppsc-ds-header__navbar {
    z-index: auto;
  }

  .ppsc-ds-header__drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-s);
    padding: var(--space-xs) var(--space-s);
    border-bottom: var(--border-width-default) solid var(--border-default);
  }

  .ppsc-ds-header__nav-list {
    position: relative; /* containing block for an exiting panel */
    flex-direction: column;
    gap: 0;
  }
  .ppsc-ds-header__nav-item {
    flex-direction: column;
  }
  .ppsc-ds-header__nav-item + .ppsc-ds-header__nav-item {
    border-top: var(--border-width-default) solid var(--border-default);
  }
  /* A sub-panel replaces the list: the other items and the open
     item's own trigger step out, the panel flows in their place. */
  .ppsc-ds-header__nav-item[data-sibling-open="true"] {
    display: none;
  }
  .ppsc-ds-header__nav-link[aria-expanded="true"] {
    display: none;
  }
  .ppsc-ds-header__nav-link {
    justify-content: space-between;
    min-height: var(--space-2xl);
    padding: 0 calc(var(--space-s) + var(--space-2xs));
    font-size: var(--step-0);
    text-align: left;
  }
  /* Drawer rows do not take a hover state. */
  .ppsc-ds-header__nav-link:hover {
    background: transparent;
    color: var(--text-default);
  }
  .ppsc-ds-header__nav-chevron {
    display: none;
  }
  .ppsc-ds-header__nav-arrow {
    display: flex;
  }

  .ppsc-ds-header__drawer-footer {
    display: flex;
    flex-direction: column;
    gap: var(--space-s);
    padding: var(--space-s);
    background: var(--surface-neutral);
    border-top: var(--border-width-default) solid var(--border-default);
    border-bottom-left-radius: var(--radius-l);
  }

  /* Mega menu as a drawer sub-panel. Panels flow inline here, so a
     closed one must leave the layout — display, not the desktop
     cross-fade. Opening slides the panel in from the right; Go back
     (ppsc-ds.js) sets data-closing to slide it out to the right, then
     data-returning on the list slides the list back in from the left.
     Keyframes are in ORGANISMS > Header. */
  .ppsc-ds-mega-menu {
    display: none;
    position: static;
    overflow: visible;
    border-radius: 0;
    visibility: visible;
    opacity: 1;
    transform: none;
    transition: none;
  }
  .ppsc-ds-mega-menu[data-open="true"] {
    display: block;
    transition: none;
    animation: ppsc-ds-drawer-panel-in var(--ppsc-ds-drawer-panel-duration)
      var(--ppsc-ds-drawer-panel-ease);
  }
  /* Go back is a CROSS-slide: the list is put back in the layout and
     slides in from the left while the panel slides out to the right
     over the top of it. That needs the panel lifted out of the flow,
     so restoring the list cannot shove it around — and it needs its
     own display, because closePanels() has already switched data-open
     off by the time this state is written. Run in sequence instead, the
     drawer would stand empty for the length of both animations. */
  .ppsc-ds-mega-menu[data-closing="true"] {
    display: block;
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    z-index: 1;
    animation: ppsc-ds-drawer-panel-out var(--ppsc-ds-drawer-panel-duration)
      var(--ppsc-ds-drawer-panel-ease) forwards;
  }
  .ppsc-ds-header__nav-list[data-returning="true"] {
    animation: ppsc-ds-drawer-list-in var(--ppsc-ds-drawer-panel-duration)
      var(--ppsc-ds-drawer-panel-ease);
  }

  .ppsc-ds-mega-menu__back-row {
    display: flex;
    align-items: center;
    min-height: var(--ppsc-ds-mega-menu-back-row-height);
    padding: var(--space-2xs) var(--space-s);
    background: var(--surface-neutral);
  }
  .ppsc-ds-mega-menu__back {
    display: flex;
    align-items: center;
    padding: 0;
    background: transparent;
    border: 0;
    color: var(--text-default);
    font-family: var(--font-family);
    font-weight: var(--font-regular);
    font-size: var(--step--1);
    line-height: 1.2;
    cursor: pointer;
  }
  .ppsc-ds-mega-menu__back:focus-visible {
    outline: 2px solid var(--ppsc-ds-focus-ring);
    outline-offset: 2px;
  }
  .ppsc-ds-mega-menu__back-icon {
    display: flex;
    width: 24px;
    height: 24px;
  }
  .ppsc-ds-mega-menu__back-icon svg {
    width: 100%;
    height: 100%;
  }

  /* The drawer has its own fixed gutter — the section clamp is
     viewport-based and would grow inside a 444px drawer. */
  .ppsc-ds-mega-menu__body {
    padding: var(--space-s) 0;
  }
  .ppsc-ds-mega-menu__container {
    flex-direction: column;
    gap: 0;
  }
  .ppsc-ds-mega-menu__feature {
    display: none;
  }
  .ppsc-ds-mega-menu__groups {
    display: flex;
    flex-direction: column;
    gap: var(--space-s);
    padding-inline: var(--space-s);
  }
  /* Explore: the link lists read as one column, only the divider
     keeps the wider space-s either side (the divider only exists in
     --continuous panels, so its margin makes up the difference). */
  .ppsc-ds-mega-menu__groups--continuous {
    gap: var(--space-2xs);
  }
  .ppsc-ds-mega-menu__divider {
    margin-block: calc(var(--space-s) - var(--space-2xs));
    margin-inline: calc(-1 * var(--space-s));
  }
  .ppsc-ds-mega-menu__group {
    gap: var(--space-2xs);
  }
  .ppsc-ds-mega-menu__group-heading {
    padding-inline: 0;
  }
  .ppsc-ds-mega-menu__list {
    gap: var(--space-2xs);
  }
  .ppsc-ds-mega-menu__link {
    padding: var(--space-xs) 0;
    border-radius: 0;
  }
  .ppsc-ds-mega-menu__link:hover {
    background: transparent;
  }
  /* Explore: plain links line up with the icon links' text (icon +
     space-2xs). Nested links sit space-s apart, per Figma mobile. */
  .ppsc-ds-mega-menu__list--nested {
    gap: var(--space-s);
    margin-top: var(--space-2xs);
    padding-inline: calc(24px + var(--space-2xs));
  }
  .ppsc-ds-mega-menu__list--inset {
    padding: 0 0 0 calc(24px + var(--space-2xs));
  }

  .ppsc-ds-mega-menu__band {
    padding: var(--space-s);
  }
  .ppsc-ds-mega-menu__band-container {
    flex-direction: column;
    align-items: flex-start;
  }
  /* Tertiary Size S: no padding, step--1 label, 16px arrow. */
  .ppsc-ds-mega-menu__band-cta {
    padding: 0;
    font-size: var(--step--1);
  }
  .ppsc-ds-mega-menu__band-cta .ppsc-ds-btn__label {
    padding-inline-start: 0;
  }
  .ppsc-ds-mega-menu__band-cta-arrow {
    width: 16px;
    height: 16px;
  }
  .ppsc-ds-mega-menu__band-cta-arrow svg {
    width: 10px;
    height: 10px;
  }
}

/* --- MOBILE (<=767.98px) --- */
@media (max-width: 767.98px) {
  /* A button never outgrows its column on a phone. Labels are nowrap
     on desktop, but a long one ("Talk to an advisor about your
     situation") is wider than a 375px screen and would be clipped at
     the edge (WCAG 1.4.10 Reflow). Buttons that fit are unaffected; only
     one that would overflow wraps onto a second line. */
  .ppsc-ds-btn {
    max-width: 100%;
    white-space: normal;
  }

  /* Icon to text drops to space-2xs; the close keeps its space-s, so
     its make-up margin grows by the same amount. */
  .ppsc-ds-alert-bar__container {
    gap: var(--space-2xs);
  }
  .ppsc-ds-alert-bar__close {
    margin-left: calc(var(--space-s) - var(--space-2xs));
  }

  .ppsc-ds-section {
    padding-inline: var(--grid-gutter);
  }
}

.simple-masthead-no-image { 
	margin-bottom: 0; 
}