/* ==========================================================================
   BENTLEY UNIVERSITY — THE IMPACT OF YOUR GIVING 2025–2026
   --------------------------------------------------------------------------
   Design tokens and type scale carry over from the 2025 Impact Report
   (impact-report-2025-container.css): 16px root, 1.5 base line-height,
   Caecilia for headings, Arial for body copy.

   0.  Fonts
   1.  Tokens
   2.  Reset & base
   3.  Canvas, sections, backgrounds
   4.  Typography
   5.  Section themes
   6.  Components
       6.1  Letter          6.7   Bullets
       6.2  Hero            6.8   Result tiles
       6.3  Video block     6.9   Accent bands
       6.4  Stat lists      6.10  Rankings
       6.5  Stat rows       6.11  Logos
       6.6  Quotes & shields
   7.  Footer
   8.  Reveal
   9.  Responsive
   10. Assets
   11. Print
   ========================================================================== */


/* 0. FONTS
   --------------------------------------------------------------------------
   Caecilia from Typekit, then the locally-installed "caecilia lt std", then
   Georgia. Only the regular (n4) cut is wired up — bold and italic are
   synthesised until the matching URLs are pasted into the stubs below.
   ========================================================================== */
@font-face {
  font-family: "caecilia";
  src: url("https://use.typekit.net/af/50fb30/00000000000000007735bc3a/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3") format("woff2"),
       url("https://use.typekit.net/af/50fb30/00000000000000007735bc3a/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3") format("woff"),
       url("https://use.typekit.net/af/50fb30/00000000000000007735bc3a/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3") format("opentype");
  font-display: auto;
  font-style: normal;
  font-weight: 400;
  font-stretch: normal;
}

/* Bold — paste the fvd=n7 URLs
@font-face {
  font-family: "caecilia";
  src: url("…&fvd=n7&v=3") format("woff2"), url("…&fvd=n7&v=3") format("woff"), url("…&fvd=n7&v=3") format("opentype");
  font-display: auto; font-style: normal; font-weight: 700; font-stretch: normal;
}
*/

/* Italic — paste the fvd=i4 URLs
@font-face {
  font-family: "caecilia";
  src: url("…&fvd=i4&v=3") format("woff2"), url("…&fvd=i4&v=3") format("woff"), url("…&fvd=i4&v=3") format("opentype");
  font-display: auto; font-style: italic; font-weight: 400; font-stretch: normal;
}
*/


/* 1. TOKENS
   ========================================================================== */
:root {
  /* --- Bentley brand palette (carried over from the 2025 report) --------- */
  --yellow:           #FFCC33;
  --royalblue:        #0075BE;
  --chartreuse:       #B3C642;
  --dark:             #313131;
  --red:              #D0433A;
  --orange:           #F99F1C;
  --salmon:           #F36F45;
  --bluegray:         #B3C4CC;
  --aqua:             #3DABA4;
  --slate:            #7B858F;
  --accessible-slate: #6C757F;
  --lightblue:        #82BCE5;
  --darkblue:         #294567;
  --white:            #FFFFFF;
  --white50:          #FFFFFF90;

  /* --- 2026 section grounds, sampled from the r3a comp ------------------- */
  --navy-deep:   #002339;   /* record-breaking / rankings */
  --sky:         #4495CB;   /* hero */
  --teal:        #279992;   /* scholarship */
  --tech-dark:   #0D1420;   /* technology */
  --purple:      #625094;   /* giving day */
  --field-dark:  #080B0C;   /* athletics */
  --service:     #1F6FC4;   /* service-learning */
  --gold:        #FAD051;   /* first-gen */

  /* --- Per-section theming, overridden by .theme-* ----------------------- */
  --accent-w:       8px;                        /* band accent rule — width   */
  --accent-gap:     clamp(1.5rem, 3cqi, 3rem);  /* rule to copy               */
  --accent-overhang: 1rem;                      /* extends past the copy      */

  --bg:          var(--navy-deep);
  --fg:          var(--white);
  --fg-dim:      var(--white);
  --rule:        rgba(255, 255, 255, .45);
  --band-accent: var(--royalblue);
  --quote-filter: none;

  /* --- Type ------------------------------------------------------------- */
  --primary-font: arial, sans-serif;
  --display-font: "caecilia", "caecilia lt std", serif;

  /* Scale. Max values are the 2025 report's literal rem sizes; the cqi
     midpoint equals the same number, so at the 1600px canvas each clamp
     resolves to exactly its rem value and only scales down below that. */
  --fs-hero:   clamp(2.5rem, 5.75cqi, 5.75rem);
  --fs-year:   clamp(1.5rem, 2.125cqi, 2.125rem);
  --fs-title:  clamp(1.75rem, 3cqi, 3rem);        /* .report-main h2  */
  --fs-intro:  clamp(1.2rem, 2cqi, 2rem);
  --fs-stat:   clamp(2.25rem, 4cqi, 4rem);        /* .stat-number     */
  --fs-band:   clamp(1.5rem, 2.5cqi, 2.5rem);     /* .report-main h3  */
  --fs-body:   1.375rem;                          /* 22px             */
  --fs-source: 1.125rem;                          /* .stat-source     */

  /* --- Space ------------------------------------------------------------ */
  --sp-1: .5rem;
  --sp-2: 1rem;
  --sp-3: 1.75rem;
  --sp-4: 2.75rem;
  --sp-5: 4rem;
  --section-pad: clamp(4.5rem, 9cqi, 9rem);

  /* The single gap between the last heading in a section and the content
     that follows it — whether that heading is the h2, the intro, or a
     subhead. Keeps the rhythm identical in sections that skip any of them. */
  --space-to-content: 6rem;

  /* --- Structure -------------------------------------------------------- */
  --report-width: 1600px;
  --page-bg:      #222222;
  --wrap:         1224px;
  --wrap-wide:    1376px;   /* hero video runs wider than the text column */
  --wrap-letter:  1070px;
}


/* 2. RESET & BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
  font-size: 16px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--page-bg);
  color: var(--fg);
  font-family: var(--primary-font);
  font-size: var(--fs-body);
  font-weight: normal;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img { display: block; max-width: 100%; height: auto; }
h1, h2, h3 { margin: 0; font-family: var(--display-font); line-height: 1.25; }
p  { margin: 0 0 var(--sp-2); font-family: var(--primary-font); line-height: 1.5; }
ul { margin: 0; padding: 0; list-style: none; }
blockquote, figure { margin: 0; }
cite { font-style: italic; }

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  text-decoration-skip-ink: auto;
}

:focus-visible { outline: 3px solid var(--royalblue); outline-offset: 3px; }

/* keeps a hyphenated term from splitting across lines */
.nowrap { white-space: nowrap; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  padding: .75rem 1.25rem; background: var(--yellow); color: var(--dark);
  font-weight: bold; text-decoration: none;
}
.skip-link:focus { left: 0; }


/* 3. CANVAS, SECTIONS, BACKGROUNDS
   ========================================================================== */
.report {
  width: 100%;
  max-width: var(--report-width);
  /* cqi is avoided here: .report is itself the query container, so container
     units on it would fall back to viewport size. Plain rem instead. */
  margin: 0 auto clamp(2.5rem, 5vw, 5rem);   /* page ground shows below */
  background: var(--navy-deep);
  overflow: hidden;
  container-type: inline-size;
}

.section {
  position: relative;
  isolation: isolate;
  padding: var(--section-pad) 0;
  background: var(--bg);
  color: var(--fg);
  overflow: hidden;
}

.wrap {
  position: relative;
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 3cqi, 2.5rem);
}
.wrap--wide   { max-width: var(--wrap-wide); }
.wrap--letter { max-width: var(--wrap-letter); }

/* `cover` scales by max(container_w / image_w, container_h / image_h). Every
   piece of art here is taller than its section at full width, so that max is
   the *width* ratio — meaning cover resolves to exactly "100% wide, natural
   height, anchored top". The compositions stay intact (whole falcon, full
   WELCOME banner) just as they did under `100% auto`.

   The difference shows up as the window narrows: text rewraps, sections grow
   taller, and the scaled art eventually stops reaching the bottom. At that
   point the height ratio wins and cover scales up to fill, cropping left and
   right instead of leaving a gap. `100% auto` had no such fallback. */
.section__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--bg-image, none);
  background-size: var(--bg-size, cover);
  background-position: var(--bg-position, center top);
  background-repeat: no-repeat;
}
/* The supplied artwork is already colour-treated, so scrims stay light —
   raising these double-tints the photography. */
.section__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--scrim, transparent);
}


/* 4. TYPOGRAPHY
   ========================================================================== */
.section__title {
  margin: 0 0 var(--sp-3);   /* tight when an intro follows */
  font-family: var(--display-font);
  font-size: var(--fs-title);
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
  text-transform: uppercase;
}

/* No intro follows — the title itself carries the full gap to content */
.section__title:not(:has(+ .section__intro)) { margin-bottom: var(--space-to-content); }

/* A styled paragraph, not a heading — these run too long to announce as one */
.section__intro {
  max-width: 65ch;
  margin: 0 auto var(--space-to-content);
  font-family: var(--display-font);
  font-size: var(--fs-intro);
  font-style: italic;
  font-weight: 400;
  line-height: 1.35;
  text-align: center;
  text-wrap: balance;
  color: var(--fg-dim);
}

.subhead {
  margin: var(--sp-5) 0 var(--sp-4);
  font-family: var(--display-font);
  font-size: var(--fs-intro);   /* matches the section intros */
  font-weight: 700;
  line-height: 1.5;
  text-align: center;
  text-transform: uppercase;
}

.bodycopy { max-width: 62ch; margin: 0 0 var(--sp-5); line-height: 1.5; }

.footnote {
  margin: var(--sp-3) 0;
  font-size: var(--fs-source);
  line-height: 1.5;
  color: var(--fg-dim);
}


/* 5. SECTION THEMES
   ========================================================================== */
.theme-navy { --bg: var(--navy-deep); }

.theme-teal {
  --bg: var(--teal);
  --scrim: linear-gradient(180deg, rgba(6,60,58,.10), rgba(6,60,58,.20));
}
.theme-tech {
  --bg: var(--tech-dark);
  --scrim: linear-gradient(180deg, rgba(4,10,20,.42), rgba(4,10,20,.58));
}
.theme-purple {
  --bg: var(--purple);
  --scrim: linear-gradient(180deg, rgba(30,18,70,.10), rgba(30,18,70,.22));
}
/* No scrim — the source image is already near-black through the upper two
   thirds, so an overlay only muddied it */
.theme-athletics { --bg: var(--field-dark); }
.theme-blue {
  --bg: var(--service);
  --scrim: linear-gradient(180deg, rgba(8,40,88,.14), rgba(8,40,88,.26));
}
.theme-gold {
  --bg: var(--gold);
  --fg: var(--dark);
  --fg-dim: var(--dark);
  --rule: rgba(49, 49, 49, .45);
  --quote-filter: brightness(0) opacity(.82);  /* white quote mark → dark */
  --scrim: linear-gradient(180deg, rgba(250,208,81,.10), rgba(236,190,60,.22));
}


/* 6. COMPONENTS
   ========================================================================== */

/* 6.1 Letter — white panel ------------------------------------------------ */
/* Sits on the same ground as the page around the report canvas */
.section--letter {
  --bg: var(--page-bg);
  --fg: var(--white);
  padding-block: clamp(3rem, 5.5cqi, 5.5rem);
  color: var(--white);
}
.section--letter p { margin-bottom: 1.5em; }
.section--letter a { color: var(--lightblue); }   /* royalblue fails on navy */
.letter__sig { margin-bottom: 0; }
.letter__sig strong { font-weight: bold; }


/* 6.2 Hero ---------------------------------------------------------------- */
.section--hero {
  --bg: var(--sky);
  padding-block: clamp(3.5rem, 8cqi, 8.5rem) clamp(2rem, 4.5cqi, 4.5rem);
  text-align: center;
}
.hero__year {
  margin: 0 0 .1em;
  font-family: var(--display-font);
  font-size: var(--fs-year);
  font-weight: 700;
  line-height: 1.25;
  color: #12304F;
}
.hero__title {
  margin: 0 auto;
  font-family: var(--display-font);
  font-size: var(--fs-hero);
  font-weight: 700;
  line-height: 1.1;
  text-transform: uppercase;
  color: var(--white);
}


/* 6.3 Video block --------------------------------------------------------- */
.videoblock {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  margin-top: clamp(2rem, 4.5cqi, 4.5rem);
  background-color: #D6D6D6;
  background-image: var(--media-image, none);
  background-size: cover;
  background-position: center;
}
.videoblock span {
  font-family: var(--primary-font);
  font-size: clamp(1.1rem, 1.75cqi, 1.75rem);
  color: #8D8D8D;
}

/* The embed ships at a fixed 608x402; stretching it to fill the container
   makes it fluid, and the container's aspect-ratio holds the shape. */
.videoblock__frame {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}
.videoblock--inline { margin-top: 0; aspect-ratio: 16 / 8.5; }


/* 6.4 Stat lists — stacked, left aligned ---------------------------------- */
.statlist { display: grid; gap: clamp(1.5rem, 2.5cqi, 2.5rem); }

.statlist__num {
  display: block;
  font-family: var(--display-font);
  font-size: var(--fs-stat);
  font-weight: 400;
  line-height: 1;
}
.statlist__label {
  display: block;
  margin-top: .35em;
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--fg-dim);
}

.twocol {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5cqi, 5rem);
  align-items: start;
}
.twocol--tech { grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); align-items: center; }


/* 6.5 Stat rows — side by side, divided by hairlines ---------------------- */
/* Equal columns, content centred in each — which puts the shared cell edge
   (and therefore the divider) exactly midway between adjacent stats. */
.statrow { display: grid; margin-bottom: clamp(2.5rem, 5.5cqi, 5.5rem); }
.statrow--2 { grid-template-columns: repeat(2, 1fr); }
.statrow--3 { grid-template-columns: repeat(3, 1fr); }

.statrow__item {
  padding: .35rem clamp(1rem, 2cqi, 2rem);
  text-align: center;
}
.statrow__item + .statrow__item { border-left: 1px solid var(--rule); }

.statrow__num {
  display: block;
  font-family: var(--display-font);
  font-size: var(--fs-stat);
  font-weight: 400;
  line-height: 1;
}
.statrow__label {
  display: block;
  margin-top: .3em;
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--fg-dim);
}


/* 6.6 Quotes & shield portraits ------------------------------------------ */
.quote {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(1.5rem, 3.25cqi, 3.25rem);
  align-items: start;
  margin-bottom: clamp(2.5rem, 5.5cqi, 5.5rem);
}

/* Opening quote mark — supplied SVG, recoloured on light grounds */
.quote__body::before,
.quote--nophoto::before {
  content: "";
  display: block;
  width: clamp(2.2rem, 3.44cqi, 3.44rem);
  aspect-ratio: 55 / 56;
  margin-bottom: clamp(1rem, 1.75cqi, 1.75rem);
  background: url("../images/quote-mark-white.svg") center / contain no-repeat;
  filter: var(--quote-filter);
}

.quote__text {
  margin: 0 0 1.15em;
  font-style: italic;
  line-height: 1.6;
}
.quote__attrib {
  font-size: var(--fs-body);
  font-weight: bold;
  font-style: normal;
  line-height: 1.5;
}

.quote--nophoto { display: block; max-width: 78ch; }

/* Shield portraits ship pre-masked with the outline baked in, so no
   clipping or border here — just size them. */
.shield {
  width: clamp(120px, 12.2cqi, 195px);
  height: auto;
}


/* 6.6b Water on glass ------------------------------------------------------
   Behaviour taken from the reference footage rather than its density: droplets
   stall for long stretches and then let go without warning.

   Each droplet is a .glass__unit holding two siblings: a .glass__trail fixed
   in place and revealed top-down by animating a clip-path inset, and a
   .glass__drop translating down over it. Keeping them separate means the track
   stays put on the glass while the bead pulls away from it — a trail parented
   to the bead would travel with it and read as a comet tail.

   No backdrop-filter here, deliberately. It samples a *backdrop root*, which
   `isolation: isolate` on the section establishes — and since the section is
   overflow:visible so beads can slide past its bottom edge, anything crossing
   that boundary sampled an empty backdrop root, which is transparent black.
   That was the flicker. The beads are painted with gradients and inset shadows
   instead, which also spares 11 animated backdrop layers.

   Timing does the rest. A keyframe's easing governs the segment that follows
   it, so a hold step gets `ease-in` (starts at a standstill, accelerates away =
   sudden release) and the end of a slip gets `ease-out` (settles into the next
   stall). Six staging posts, all randomised per droplet. */
.section--athletics { overflow: visible; }

.glass {
  position: absolute;
  inset: 0;
  overflow: visible;      /* droplets slide on past, behind the next section */
  pointer-events: none;
}

.glass__unit {
  position: absolute;
  top: var(--y);
  left: var(--x);
  width: 0;
  height: 0;
}

/* --- the wet track ------------------------------------------------------- */
.glass__trail {
  position: absolute;
  left: 50%;
  top: 0;                           /* runs DOWN from the start point */
  width: var(--w);                  /* exactly as wide as its bead */
  height: var(--fall);
  transform: translateX(-50%);
  border-radius: 40% 60% 55% 45% / 1% 1% 1% 1%;
  clip-path: inset(0 0 100% 0);     /* nothing revealed yet */
  /* uneven stops read as beading; a smooth ramp reads as a drawn stroke */
  background: linear-gradient(180deg,
    rgba(255,255,255,0)    0%,
    rgba(255,255,255,.018)  7%,
    rgba(255,255,255,.06)  13%,
    rgba(255,255,255,.015) 19%,
    rgba(255,255,255,.068) 28%,
    rgba(255,255,255,.024) 36%,
    rgba(255,255,255,.08)  47%,
    rgba(255,255,255,.03)  56%,
    rgba(255,255,255,.092) 68%,
    rgba(255,255,255,.036) 78%,
    rgba(255,255,255,.11)  91%,
    rgba(255,255,255,.072) 100%);
  animation: glass-trail var(--t) linear var(--d) infinite;
  animation-play-state: paused;
}

/* --- the bead ------------------------------------------------------------ */
.glass__drop {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--w);
  height: calc(var(--w) * var(--hr));
  margin-left: calc(var(--w) / -2);
  opacity: 0;
  border-radius: var(--r);
  background:
    radial-gradient(ellipse at 62% 70%, rgba(255,255,255,.80) 0%, rgba(255,255,255,.24) 36%, rgba(255,255,255,0) 62%),
    radial-gradient(ellipse at 36% 24%, rgba(0,0,0,.26) 0%, rgba(0,0,0,0) 56%),
    rgba(255, 255, 255, .07);
  box-shadow:
    inset 0  1.5px 3px rgba(0,0,0,.38),
    inset 0 -2px 2.5px rgba(255,255,255,.45),
    0 -1px 3px rgba(0,0,0,.26);
  animation: glass-slide var(--t) linear var(--d) infinite;
  animation-play-state: paused;
  will-change: transform, opacity;
}

/* Hold, release, slip, stall — six times over. No fade at the end: the bead
   keeps full opacity and simply passes behind the next section, so the reset
   at 0% is never seen. */
@keyframes glass-slide {
  0%   { transform: translateY(0); opacity: 0;        animation-timing-function: ease-out; }
  4%   { transform: translateY(0); opacity: var(--o); animation-timing-function: ease-in; }
  16%  { transform: translateY(calc(var(--fall) * var(--c1))); animation-timing-function: ease-out; }
  23%  { transform: translateY(calc(var(--fall) * var(--c1))); animation-timing-function: ease-in; }
  34%  { transform: translateY(calc(var(--fall) * var(--c2))); animation-timing-function: ease-out; }
  40%  { transform: translateY(calc(var(--fall) * var(--c2))); animation-timing-function: ease-in; }
  51%  { transform: translateY(calc(var(--fall) * var(--c3))); animation-timing-function: ease-out; }
  56%  { transform: translateY(calc(var(--fall) * var(--c3))); animation-timing-function: ease-in; }
  66%  { transform: translateY(calc(var(--fall) * var(--c4))); animation-timing-function: ease-out; }
  71%  { transform: translateY(calc(var(--fall) * var(--c4))); animation-timing-function: ease-in; }
  80%  { transform: translateY(calc(var(--fall) * var(--c5))); animation-timing-function: ease-out; }
  84%  { transform: translateY(calc(var(--fall) * var(--c5))); animation-timing-function: ease-in; }
  93%  { transform: translateY(calc(var(--fall) * var(--c6))); animation-timing-function: ease-out; }
  96%  { transform: translateY(calc(var(--fall) * var(--c6))); animation-timing-function: ease-in; }
  100% { transform: translateY(var(--fall)); opacity: var(--o); }
}

/* Identical stops and easing, so the track's leading edge stays under the bead */
@keyframes glass-trail {
  0%   { clip-path: inset(0 0 100% 0); opacity: 0;   animation-timing-function: ease-out; }
  4%   { clip-path: inset(0 0 100% 0); opacity: .85; animation-timing-function: ease-in; }
  16%  { clip-path: inset(0 0 calc(100% - var(--c1) * 100%) 0); animation-timing-function: ease-out; }
  23%  { clip-path: inset(0 0 calc(100% - var(--c1) * 100%) 0); animation-timing-function: ease-in; }
  34%  { clip-path: inset(0 0 calc(100% - var(--c2) * 100%) 0); animation-timing-function: ease-out; }
  40%  { clip-path: inset(0 0 calc(100% - var(--c2) * 100%) 0); animation-timing-function: ease-in; }
  51%  { clip-path: inset(0 0 calc(100% - var(--c3) * 100%) 0); animation-timing-function: ease-out; }
  56%  { clip-path: inset(0 0 calc(100% - var(--c3) * 100%) 0); animation-timing-function: ease-in; }
  66%  { clip-path: inset(0 0 calc(100% - var(--c4) * 100%) 0); animation-timing-function: ease-out; }
  71%  { clip-path: inset(0 0 calc(100% - var(--c4) * 100%) 0); animation-timing-function: ease-in; }
  80%  { clip-path: inset(0 0 calc(100% - var(--c5) * 100%) 0); animation-timing-function: ease-out; }
  84%  { clip-path: inset(0 0 calc(100% - var(--c5) * 100%) 0); animation-timing-function: ease-in; }
  93%  { clip-path: inset(0 0 calc(100% - var(--c6) * 100%) 0); animation-timing-function: ease-out; }
  96%  { clip-path: inset(0 0 calc(100% - var(--c6) * 100%) 0); animation-timing-function: ease-in; }
  100% { clip-path: inset(0 0 0 0); opacity: .85; }
}

#athletics.is-inview .glass__drop,
#athletics.is-inview .glass__trail { animation-play-state: running; }


/* 6.7 Bullets ------------------------------------------------------------- */
.bullets {
  display: grid;
  gap: clamp(1.1rem, 2cqi, 2rem);
  max-width: 74ch;
  margin: 0 auto clamp(2.5rem, 5.5cqi, 5.5rem);
}
.bullets li { position: relative; padding-left: clamp(1.75rem, 2.75cqi, 2.75rem); line-height: 1.5; }
.bullets li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .42em;
  width: .625em;
  height: .625em;
  background: var(--royalblue);
}
.bullets strong { font-weight: bold; }


/* 6.8 Result tiles -------------------------------------------------------- */
/* Tracks are sized to the tile rather than stretched across the wrap, so the
   grid stays a tight centred block instead of spreading to the full column */
.tiles {
  --tile-size: 245px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, var(--tile-size)));
  justify-content: center;
  gap: clamp(.6rem, 1.1cqi, 1.1rem);
  margin-bottom: clamp(2.5rem, 5.5cqi, 5.5rem);
}
.tile {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  aspect-ratio: 1;                 /* square tiles */
  padding: clamp(.9rem, 1.5cqi, 1.5rem) clamp(.75rem, 1.25cqi, 1.25rem);
  background: var(--royalblue);
  color: var(--white);
  text-align: center;
}
.tile__sport {
  display: block;
  margin-bottom: .5em;
  font-weight: bold;
  line-height: 1.35;
}
.tile__result { display: block; font-size: var(--fs-source); line-height: 1.5; }


/* 6.9 Accent bands --------------------------------------------------------
   One component, two states. The band always spans the full content width so
   every callout reads the same length down the page; the only difference is
   whether a figure claims the right-hand column. Copy fills whatever space is
   left, so no width needs to be hand-tuned per section. */
.band {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(1.5rem, 3.25cqi, 3.25rem);
  align-items: center;
  margin-top: clamp(2.5rem, 5cqi, 5rem);
  padding-left: calc(var(--accent-w) + var(--accent-gap));
}

/* The rule is anchored to the copy, not the band, so it always sits a fixed
   amount taller than the text — regardless of whether a figure makes the band
   itself taller. Same width everywhere; only the length follows the words. */
.band__copy { position: relative; }
.band__copy::before {
  content: "";
  position: absolute;
  left: calc(-1 * (var(--accent-w) + var(--accent-gap)));
  top: calc(-1 * var(--accent-overhang));
  bottom: calc(-1 * var(--accent-overhang));
  width: var(--accent-w);
  background: var(--band-accent);
}

/* Band headings are Arial bold, not Caecilia — matching the comp */
.band__title {
  margin: 0 0 .45em;
  font-family: var(--primary-font);
  font-size: var(--fs-band);
  font-weight: bold;
  line-height: 1.25;
  text-transform: uppercase;
}
/* The rule under a callout heading is drawn rather than a text-decoration, so
   it can wipe in left to right when the band scrolls into view. It defaults to
   fully drawn — only the reveal script (which adds .js-reveal) retracts it, so
   with JS off or reduced motion on, the underline is simply there. */
.band__title a { position: relative; text-decoration: none; }
.band__title a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 2px;
  background: currentColor;
  transform: scaleX(1);
  transform-origin: left center;
  transition: transform .25s ease-out;
}
/* .is-inview is toggled both ways, unlike the one-shot .is-visible used for
   the fade-ins — so the rule redraws every time the band comes back on screen.
   It retracts immediately on exit; only the draw is delayed. */
.js-reveal .band .band__title a::after { transform: scaleX(0); }
.js-reveal .band.is-inview .band__title a::after {
  transform: scaleX(1);
  transition-delay: 1s;   /* let the band settle before the rule draws */
}
/* No max-width — the copy fills its column, which is the full band when there
   is no figure and the remainder when there is one. */
.band__copy p { margin: 0; }

.band__figure { width: clamp(180px, 20cqi, 320px); height: auto; }
.band__logo   { width: clamp(180px, 24cqi, 384px); height: auto; }

/* The Falcons Forward lockup is a transparent PNG — no crop, and it reads
   larger than the photo-style band figures */
#we-did-it .band__logo { width: clamp(200px, 26cqi, 416px); }

.band--blue   { --band-accent: var(--royalblue); }
.band--cyan   { --band-accent: var(--royalblue); }
.band--pink   { --band-accent: var(--red); }
.band--lime   { --band-accent: var(--chartreuse); }
.band--cream  { --band-accent: #FFE7A0; }

/* No figure — copy takes the whole band rather than leaving a column empty */
.band:not(:has(.band__figure)):not(:has(.band__logo)) { grid-template-columns: minmax(0, 1fr); }

/* This band sits over the bright turf at the bottom of the athletics photo,
   where white type fails contrast — black holds up against the grass. */
#playing-with-purpose { color: #000; }


/* 6.10 Rankings ----------------------------------------------------------- */
.rankings {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  row-gap: clamp(2.25rem, 4.5cqi, 4.5rem);
  column-gap: clamp(1.5rem, 3.25cqi, 3.25rem);
}
.rank { text-align: center; }
.rank__num {
  display: block;
  font-family: var(--display-font);
  font-size: clamp(2rem, 3.25cqi, 3.25rem);
  font-weight: 400;
  line-height: 1;
}
.rank__label { display: block; margin-top: .3em; line-height: 1.5; }
.rank__source {
  display: block;
  margin-top: .3em;
  font-size: var(--fs-source);
  font-style: italic;
  color: var(--white50);
}


/* 6.11 Logos -------------------------------------------------------------- */
.logo--clocktower { width: clamp(160px, 17cqi, 272px); height: auto; margin-top: var(--sp-2); }


/* 7. FOOTER
   ==========================================================================
   Tall enough for the campus photo to read at full height, with the band
   sitting over the sky at the top. The "Bentley University" wordmark is part
   of footer-bg.jpg, so there's no text element for it. */
.section--footer {
  /* Solid strip below the photo, matched to the near-black at the bottom edge
     of footer-bg.jpg so it reads as an extension of the image. */
  --footer-base: clamp(7rem, 11cqi, 11rem);
  --bg: #010101;
  --band-accent: var(--royalblue);
  display: flex;
  flex-direction: column;
  min-height: calc(clamp(620px, 80cqi, 1280px) + var(--footer-base));
  /* Bottom padding is deliberately shorter than --footer-base, so the sign-off
     block drops into the black strip below the photo. */
  padding-block: clamp(3rem, 6cqi, 6rem) clamp(5rem, 9cqi, 9rem);
}
/* Photo stops short of the base strip, which is the section's own --bg */
.section--footer .section__bg { bottom: var(--footer-base); }
.section--footer .band { margin-top: 0; }

/* Sun flare -----------------------------------------------------------------
   Matches the geometry of footer-bg.jpg (bottom-anchored, full width) so the
   glow stays locked to the sun in the photo, then flickers while the page is
   being scrolled. Idle state is invisible, so the photo reads untouched. */
.sunflare {
  --sun-x: 77%;              /* sun position within the photo */
  --sun-y: 81%;
  position: absolute;
  left: 50%;
  bottom: var(--footer-base);
  z-index: -1;
  width: 100%;
  aspect-ratio: 2851 / 4045; /* footer-bg.jpg */
  transform: translateX(-50%);
  transform-origin: var(--sun-x) var(--sun-y);
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  /* Hot core, warm mid-glow, and a broad atmospheric halo */
  background:
    radial-gradient(circle at var(--sun-x) var(--sun-y),
      rgba(255, 252, 242, 1)    0%,
      rgba(255, 242, 204, .96)   3%,
      rgba(255, 217, 144, .68)   8%,
      rgba(255, 193, 100, .30)  14%,
      transparent 22%),
    radial-gradient(circle at var(--sun-x) var(--sun-y),
      rgba(255, 208, 134, .40)   0%,
      rgba(255, 188,  98, .17)  17%,
      transparent 36%);
}

/* Lens-flare streaks -------------------------------------------------------
   Anamorphic bars pinned to the sun and rotated off it, echoing the diagonal
   wisp already in the photograph. Each runs its own slow rotate/stretch cycle
   at a different duration so they drift out of phase and never read as a
   mechanical loop. They inherit the parent's opacity, so they only surface
   while the flare is active. */
.sunflare__streak {
  position: absolute;
  left: var(--sun-x);
  top: var(--sun-y);
  width: var(--len);
  height: var(--thick);
  border-radius: 50%;
  filter: blur(var(--blur));
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(255, 206, 140, 0) 8%,
    rgba(255, 219, 160, .35) 28%,
    rgba(255, 248, 226, .95) 50%,
    rgba(255, 219, 160, .35) 72%,
    rgba(255, 206, 140, 0) 92%,
    transparent 100%);
  transform: translate(-50%, -50%) rotate(var(--angle)) scaleX(1);
}

/* Long primary wisp, angled like the streak in the photo */
.sunflare__streak--a { --len: 62%; --thick: 5px;  --blur: 3px; --angle: -32deg; }
/* Classic horizontal anamorphic bar */
.sunflare__streak--b { --len: 46%; --thick: 3px;  --blur: 2px; --angle:   6deg; }
/* Short counter-diagonal */
.sunflare__streak--c { --len: 30%; --thick: 2px;  --blur: 2px; --angle:  58deg; }
/* Faint vertical spike */
.sunflare__streak--d { --len: 22%; --thick: 2px;  --blur: 3px; --angle: -78deg; }

/* The animations are always attached but start paused, so stopping the scroll
   freezes the flare exactly where it is instead of snapping it off. Resuming
   the scroll picks the cycle back up mid-stride. A paused animation still
   applies its current keyframe values, which is what holds the glow in place. */
.sunflare            { animation: sun-flicker 2.6s ease-in-out infinite paused; }
.sunflare__streak--a { animation: streak-a 5.2s ease-in-out infinite paused; }
.sunflare__streak--b { animation: streak-b 3.7s ease-in-out infinite paused; }
.sunflare__streak--c { animation: streak-c 4.4s ease-in-out infinite paused; }
.sunflare__streak--d { animation: streak-d 6.1s ease-in-out infinite paused; }

.is-scrolling .sunflare,
.is-scrolling .sunflare__streak { animation-play-state: running; }

@keyframes streak-a {
  0%, 100% { transform: translate(-50%, -50%) rotate(-36deg) scaleX(.82); }
  35%      { transform: translate(-50%, -50%) rotate(-28deg) scaleX(1.22); }
  62%      { transform: translate(-50%, -50%) rotate(-33deg) scaleX(.95); }
  81%      { transform: translate(-50%, -50%) rotate(-25deg) scaleX(1.12); }
}
@keyframes streak-b {
  0%, 100% { transform: translate(-50%, -50%) rotate(10deg) scaleX(1.18); }
  28%      { transform: translate(-50%, -50%) rotate(2deg)  scaleX(.76); }
  55%      { transform: translate(-50%, -50%) rotate(9deg)  scaleX(1.05); }
  78%      { transform: translate(-50%, -50%) rotate(3deg)  scaleX(.88); }
}
@keyframes streak-c {
  0%, 100% { transform: translate(-50%, -50%) rotate(54deg) scaleX(.7); }
  40%      { transform: translate(-50%, -50%) rotate(66deg) scaleX(1.15); }
  70%      { transform: translate(-50%, -50%) rotate(57deg) scaleX(.9); }
}
@keyframes streak-d {
  0%, 100% { transform: translate(-50%, -50%) rotate(-82deg) scaleX(1.1); }
  45%      { transform: translate(-50%, -50%) rotate(-70deg) scaleX(.65); }
  74%      { transform: translate(-50%, -50%) rotate(-79deg) scaleX(.95); }
}

/* A slow swell with two shallow gutters cut into it — enough irregularity to
   read as a live flare, not so much that it pulls focus from the page. */
@keyframes sun-flicker {
  0%,
  100% { opacity: .80; transform: translateX(-50%) scale(1.02);  }
  12%  { opacity: 1;   transform: translateX(-50%) scale(1.06);  }
  19%  { opacity: .62; transform: translateX(-50%) scale(1.005); }  /* gutter */
  27%  { opacity: .94; transform: translateX(-50%) scale(1.05);  }
  44%  { opacity: .82; transform: translateX(-50%) scale(1.03);  }
  58%  { opacity: 1;   transform: translateX(-50%) scale(1.06);  }
  66%  { opacity: .60; transform: translateX(-50%) scale(1.01);  }  /* gutter */
  74%  { opacity: .93; transform: translateX(-50%) scale(1.045); }
  90%  { opacity: .76; transform: translateX(-50%) scale(1.025); }
}


/* 7b. BREADCRUMB
   ==========================================================================
   Carried over from the 2025 report so the two years sit consistently in the
   site. Sits outside .report and spans the full viewport, and runs in normal
   flow rather than position:absolute, which here would overlap the letter. */
.breadcrumb {
  width: 100%;
  padding: 6px 12px;
  background: var(--page-bg);
  border-bottom: 1px solid #333;
  font-family: var(--primary-font);
  font-size: .8rem;
  font-weight: normal;
  line-height: 1.5;
}
.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}
.breadcrumb li { display: flex; align-items: center; color: var(--white); }
.breadcrumb li + li::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin: 0 8px;
  border-top: 2px solid var(--white);
  border-right: 2px solid var(--white);
  transform: rotate(45deg);
}
.breadcrumb a { color: #1EA8FC; text-decoration: none; }
.breadcrumb a:hover,
.breadcrumb a:focus { text-decoration: underline; }


/* Fade covering the lower part of the photo, including the sun flare. Absolute
   with z-index auto, placed before the icons in source order, so it paints
   above the flare (z-index -1) and below the icons. */
.footer-fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: clamp(180px, 22cqi, 352px);
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(1, 1, 1, 0)   0%,
    rgba(1, 1, 1, .50) 42%,
    rgba(1, 1, 1, .88) 72%,
    #010101 90%);
}

/* Wordmark + social icons, pinned to the bottom of the footer's flex column.
   position:relative keeps the pair above .footer-fade in paint order. */
.footer-signoff {
  position: relative;
  margin: auto 0 0;
  text-align: center;
}
.footer-wordmark {
  width: clamp(260px, 33cqi, 532px);
  height: auto;
  margin: 0 auto;
}
.social-icons {
  display: flex;
  justify-content: center;
  gap: 2rem;
  margin: 3rem 0 0;
}
.social-icons a img { display: block; width: 3rem; }


/* 8. REVEAL
   ========================================================================== */
.js-reveal .reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .7s ease, transform .7s ease;
}
.js-reveal .reveal.is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js-reveal .reveal { opacity: 1; transform: none; transition: none; }
  .sunflare, .is-scrolling .sunflare { animation: none; opacity: 0; }
  .sunflare__streak, .is-scrolling .sunflare__streak { animation: none; }
  .glass { display: none; }
}


/* 9. RESPONSIVE
   ========================================================================== */
@container (max-width: 900px) {
  .twocol, .twocol--tech { grid-template-columns: 1fr; }
  .tiles { grid-template-columns: repeat(2, minmax(0, var(--tile-size))); }
  .rankings { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .band { grid-template-columns: minmax(0, 1fr); }
  .band__figure, .band__logo { width: min(100%, 320px); }
}

@container (max-width: 620px) {
  .statrow--2, .statrow--3 { grid-template-columns: 1fr; row-gap: 2rem; }
  .statrow__item { padding-left: 0 !important; }
  .statrow__item + .statrow__item { border-left: 0; }
  .tiles { grid-template-columns: minmax(0, var(--tile-size)); }
  .rankings { grid-template-columns: 1fr; }
  .quote { grid-template-columns: 1fr; }
  .shield { width: 185px; margin-inline: auto; }
}


/* 10. ASSETS
   ==========================================================================
   Section backgrounds are wired through custom properties. Everything else
   (portraits, band photos, logos) is a plain <img> in the markup.
   -------------------------------------------------------------------------- */
#hero        .section__bg { --bg-image: url("../images/hero-bg.jpg"); }
#record      .section__bg { --bg-image: url("../images/falcon-bg.png"); }
#scholarship .section__bg { --bg-image: url("../images/scholarship-student-support-bg.png"); }
#tech        .section__bg { --bg-image: url("../images/technology-bg.jpg"); }
#giving-day  .section__bg { --bg-image: url("../images/falcons-forward-bg.png"); }
/* Anchored from the bottom rather than the top. With background-position, a
   percentage resolves to P x (containerH - imageH), so `calc(100% + 193px)`
   puts the image's bottom edge a constant 193px below the section's — the crop
   stays put as the section grows taller on narrower screens, instead of
   drifting the way a fixed offset from the top does.
   193px is the one number to change if the crop needs nudging. */
#athletics   .section__bg { --bg-image: url("../images/athletics-bg.jpg");   --bg-position: center calc(100% + 193px); }
#service     .section__bg { --bg-image: url("../images/service-learning-bg.jpg"); }
#first-gen   .section__bg { --bg-image: url("../images/first-generation-bg.jpg"); }

/* Footer photo is bottom-anchored and fades up into the navy of the rankings
   section above. The first stop must be FULLY opaque and exactly --navy-deep
   (#002339): at .97 the photo bled through by 3% right at the join, which read
   as a hard seam against the solid navy above. Holding it opaque through the
   first few percent gives the fade somewhere to start from. */
#lifelong .section__bg {
  --bg-image: url("../images/footer-bg.jpg");
  --bg-position: center bottom;
  --scrim: linear-gradient(180deg, rgba(0,35,57,1) 0%, rgba(0,35,57,.55) 22%, rgba(0,35,57,0) 50%);
}

/* --- Still needed --------------------------------------------------------
   • images/playing-with-purpose.jpg   Athletics band photo   (~700×486)
   • images/one-day-one-million.png    Giving Day band logo   (~920×560, transparent)
   • images/hero-video-poster.jpg      Hero video             (1920×1080)
   • images/tech-video-poster.jpg      Technology video       (1920×1080)

   Photos and logos go straight into the markup as <img>; posters wire up here:

#hero .videoblock { --media-image: url("../images/hero-video-poster.jpg"); }
#tech .videoblock { --media-image: url("../images/tech-video-poster.jpg"); }
   -------------------------------------------------------------------------- */


/* 11. PRINT
   ========================================================================== */
@media print {
  body { background: #fff; }
  .report { box-shadow: none; max-width: none; }
  .section { padding: 1.5rem 0; background: #fff !important; color: #000 !important; break-inside: avoid; }
  .section__bg, .skip-link { display: none !important; }
  .tile { background: #eee !important; color: #000 !important; aspect-ratio: auto; }
  .section--footer { min-height: 0; }
}
