/* ---- Gradient headline ink ------------------------------------------------
   Major headings are painted with a subtle dark gradient clipped to the text,
   not a flat fill. Only the headings the design marks - NOT every h1-h4. The
   step numbers, event titles, roster names, door titles, the heritage h3 and
   the officer sheet all stay flat, because the design leaves them flat.

   THE PADDING/MARGIN PAIR IS PART OF THIS RULE, NEVER SPLIT IT.
   background-clip:text paints only inside the element box, so at the tight
   display line-heights here (.95-1.1) descenders overshoot and come out
   transparent - "Scholarships" loses the tail of its p. Each heading grows its
   paint box by .14em top and bottom and gives exactly that back out of its
   own margins, so the glyph is painted whole and the layout does not move.
   Because the give-back is per-element, each selector below restates its own
   margins rather than using a shared margin-block. */
.h-sect, .h-sub, .vcard h3, .benefit h3, .gateway h3,
.hero h1, .hero__beats h1, .hero__beats h2, .inkgrad {
  background:linear-gradient(135deg,#1D1D1F 0%,#434344 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
  padding-block:.14em;
}
.h-sect      { margin:-.14em 0 calc(24px - .14em); }
.h-sub       { margin:-.14em 0 calc(16px - .14em); }
.vcard h3    { margin:-.14em 0 calc(10px - .14em); }
.benefit h3  { margin:-.14em 0; }
.gateway h3  { margin:-.14em 0 calc(20px - .14em); }
.hero h1     { margin:-.14em 0 calc(20px - .14em); }
.hero__beats h1 { margin:-.14em auto; }
.hero__beats h2 { margin:-.14em 0 calc(24px - .14em); }

/* Dusk: the ink inverts to a pale gradient. */
[data-zone="dusk"] .h-sect, [data-zone="dusk"] .h-sub,
[data-zone="dusk"] .vcard h3, [data-zone="dusk"] .benefit h3,
[data-zone="dusk"] .gateway h3, [data-zone="dusk"] .inkgrad {
  background:linear-gradient(135deg,#F5F5F7 0%,#D8C9F0 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent;
}
/* On the ramp and on any own-surface card the gradient is dropped entirely and
   the ink goes flat - a gradient needs a settled ground to read against, and
   -webkit-text-fill-color has to be reset or `color` alone does nothing. */
[data-ink="deep"] .h-sect, [data-ink="deep"] .h-sub,
[data-ink="deep"] .benefit h3, [data-ink="deep"] .inkgrad {
  background:none; -webkit-text-fill-color:#140F1C; color:#140F1C;
}
[data-zone="dusk"][data-ink="bright"] .h-sect, [data-zone="dusk"][data-ink="bright"] .h-sub,
[data-zone="dusk"][data-ink="bright"] .benefit h3, [data-zone="dusk"][data-ink="bright"] .inkgrad {
  background:none; -webkit-text-fill-color:#fff; color:#fff;
}

/* ===========================================================================
   Delta Omicron Chapter - stylesheet
   Ported from the approved design, "Delta Omicron (DSPDO).dc.html", 7 Aug 2026.

   THREE FACES, THREE JOBS, NEVER SWAPPED
     Georgia      display - headings, stat numerals, card titles
     Bebas Neue   labels and eyebrows ONLY
     Lato         body

   DO NOT ADD A SERIF WEBFONT. Playfair Display was named in ~29 stacks in the
   design and never loaded - no @font-face, no file - so every serif element
   always rendered as Georgia, and Georgia is the appearance the chapter
   reviewed and approved. The dead name was stripped on 7 Aug 2026 for exactly
   that reason. Georgia is a system font on macOS/iOS/Windows but NOT Android,
   where this chain falls to Noto Serif. Accepted.

   Georgia has 400 and 700 and nothing between. NEVER DECLARE 500 - but note
   what the design's 500s MEAN. CSS font matching resolves a requested 500
   down to 400 when only 400 and 700 exist, so every `font-weight:500` in the
   design renders as REGULAR, not bold. The design uses both weights
   deliberately and the split is not decorative:

     700  section openers (.h-sect), value cards, benefit titles,
          "Begin Your Journey.", the narrow-hero h1, the officer sheet
     400  everything the design writes as 500 - the pinned hero beats,
          all .h-sub subheads, step titles, event titles, roster names,
          door titles, the roll title, the heritage h3, the footer goldline

   Reading a 500 as "closer to 700 than 400" is backwards and made every
   subhead on the page bold. Check the design's declared number, then map it.
   =========================================================================== */

@font-face { font-family:'Bebas Neue'; src:url('../fonts/bebas-neue.woff2') format('woff2'); font-weight:400; font-display:swap; }
@font-face { font-family:'Lato'; src:url('../fonts/lato-regular.woff2') format('woff2'); font-weight:400; font-display:swap; }
@font-face { font-family:'Lato'; src:url('../fonts/lato-bold.woff2') format('woff2'); font-weight:700; font-display:swap; }

:root {
  --font-display:'Georgia','Iowan Old Style','Times New Roman',serif;
  --font-label:'Bebas Neue',sans-serif;
  --font-body:'Lato',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;

  /* Canvas: one dawn-to-dusk gradient down the whole document. The page IS the
     gradient and contains both ends, which is why there is no dark mode. */
  --canvas-ombre:linear-gradient(180deg,#FAF8FF 0%,#FFF3D1 45%,#2A1448 80%,#140726 100%);

  --purple:#330066; --purple-deep:#2A0B4E; --purple-mid:#4A1A86;
  --purple-royal:#3B1B6E; --purple-night:#1D0B38; --purple-bright:#42107d;
  --gold:#F6C445; --gold-old:#E3C15B; --gold-cream:#FFF5D6; --gold-pale:#EFD9A2;
  --gold-deep:#8f6804; --gold-darkest:#6B4A02; --gold-signal:#F7B910;
  --rose:#C40043; --blue:#0038A8;

  --ink:#1a1524; --ink-slate:#1D1D1F; --ink-secondary:#56505f;
  --ink-muted:#57516B; --ink-invert:#F5F5F7;

  --glass-surface:rgba(255,255,255,.78);
  --glass-blur:blur(28px) saturate(180%);
  --mat-thin:blur(18px) saturate(170%);
  --squircle:36px;
  --ease:cubic-bezier(.16,1,.3,1);
  --hit:44px;
  /* The page gutter. Containers add it to their max-width rather than eating
     into it - see the note on .wrap. */
  --gutter:26px;
  --gold-metallic:linear-gradient(135deg,#FFF5D6 0%,#F6C445 50%,#E3C15B 100%);
}

html { scroll-behavior:smooth; background:var(--canvas-ombre); background-color:#1B0B30; }
body { margin:0; font-family:var(--font-body); color:var(--ink);
  -webkit-font-smoothing:antialiased; overflow-x:hidden; background:transparent; }
* { box-sizing:border-box; }
/* height:auto is NOT optional next to max-width. Every mark on this site
   carries width/height attributes for aspect-ratio reservation, and any rule
   that sets only `width` then leaves the ATTRIBUTE height in force: the flag
   in the heritage card rendered 104x736 - a 1213x736 horizontal badge crushed
   into a vertical sliver - for exactly this reason. */
img { display:block; max-width:100%; height:auto; }
a { color:var(--purple); text-decoration:none; }
a:hover { color:var(--purple-mid); }
input, select, textarea, button { font-family:inherit; }
::selection { background:var(--gold-signal); color:#24004a; }
:focus-visible { outline:3px solid var(--gold-signal); outline-offset:3px; border-radius:6px; }

h1,h2,h3,h4 { font-family:var(--font-display); letter-spacing:-.02em; margin:0; }
p { margin:0; }
.skip-link { position:absolute; left:-9999px; }
.skip-link:focus { left:12px; top:12px; z-index:200; background:#fff; padding:12px 18px; border-radius:12px; }

/* ---- Motion -------------------------------------------------------------- */
@keyframes fadeUp { from { opacity:0; transform:translateY(16px);} to { opacity:1; transform:none; } }
@keyframes tickerUnpack { from { opacity:0; transform:translateY(18px) scale(.98);} to { opacity:1; transform:none; } }
@keyframes goldDrift { 0% { background-position:0% 50%; } 100% { background-position:200% 50%; } }
/* .reveal is an INERT MARKER. All item motion is driven by revealItems()
   through [data-in] further down this file - one system, not two.

   This rule used to read `.reveal { opacity:0; transform:translateY(24px); }`
   and waited for reveals() in site.js to add .is-in. reveals() is never
   called, so all 77 nodes carrying the class - every value card, every step
   card, the heritage card, the AUG 24 tabling card, the "Built for leaders"
   card, every officer card, the impact figures, the faculty roll - sat at
   opacity 0 permanently. The page read as though the content had never been
   written. Only elements WITHOUT .reveal (the event rows, the rail tiles, the
   countdown, the footer links) were ever visible, which is exactly the set
   that survived the side-by-side comparison.
   Do not give this class an opacity again, and do not wire reveals() back up
   alongside revealItems() - two observers on the same node animate it twice. */
.reveal { }
@media (prefers-reduced-motion:reduce) {
  html { scroll-behavior:auto; }
  .ticker, .goldmark, .goldline { animation:none !important; }
  .lift { transition:none; }
}

/* ---- Layout --------------------------------------------------------------
   THE MAX-WIDTH IS THE CONTENT MEASURE, so the gutters are added on top.

   The design splits this across two elements: the section carries
   `padding:150px 26px 160px` and an inner div carries `max-width:1180px`
   with no padding, so the content box is a full 1180. We do both jobs on one
   element - and with `* { box-sizing:border-box }` that puts the padding
   INSIDE the max-width, which was costing every container exactly 52px:
   1180 measured 1128, 1080 measured 1028, 840 measured 788. It showed up as
   the whole page scrunched toward the centre, and it compounded in grids -
   a four-across value row lost 13px per column.

   Keep the gutter in the calc. Writing 1232 as a bare number here is how the
   next person loses the 52px again. */
.wrap { max-width:calc(1180px + 2 * var(--gutter)); margin:0 auto; padding:0 var(--gutter); }
.wrap--narrow { max-width:calc(1080px + 2 * var(--gutter)); }
.wrap--tight { max-width:calc(840px + 2 * var(--gutter)); }
.sect { padding:150px 0; position:relative; }
.sect--tight { padding:104px 0 40px; }

/* EYEBROWS ARE CHAPTER PURPLE ON THE SHEET, not gold. The design sets
   `[data-eyebrow] { color:#330066 !important }` globally, which overrides the
   per-eyebrow colours written inline on the markup - so the rose on
   RECRUITMENT and the blue on WHAT IT GIVES YOU never paint. Purple is what
   renders, and it measures ~10:1 on the pale sheet.

   The full ladder, all of it the design's:
     sheet, light half      #330066   (here)
     ramp, light side       #3A2405   [data-ink="deep"]
     dusk                   #DFCA8B   [data-zone="dusk"]
     ramp, dark side        #FFEDBC   [data-zone="dusk"][data-ink="bright"]
     inside a card          #8f6804   own-surface rules at the bottom
   An earlier pass here substituted --gold-darkest to claw back contrast on the
   champagne band. That was solving a problem the design had already solved. */
.eyebrow { font-family:var(--font-label); font-size:14px; letter-spacing:.34em;
  color:var(--purple); margin:0 0 22px; }
.h-sect { font-size:clamp(33px,5.4vw,68px); line-height:1; letter-spacing:-.04em;
  font-weight:700; color:var(--ink); margin:0 0 24px; text-wrap:balance; }
.h-sub { font-family:var(--font-display); font-size:clamp(26px,3.8vw,52px);
  line-height:1.06; letter-spacing:-.03em; font-weight:400; color:var(--ink); margin:0 0 16px; }
/* THE DESIGN SETS LEADING AND SIZE PER INSTANCE, not once on a class. This
   base is the most common of the seven - clamp(17px,1.9vw,20px)/1.6, used by
   "Here since 1959" and the off-season gateway note. The other five each
   override it inline, and the override is the design's value, not a tweak:

     #join          clamp(17px,1.9vw,21px) / 1.55 / 52ch
     dark beat      clamp(16px,1.7vw,19px) / 1.7  / 50ch
     #chapter       clamp(17px,2vw,23px)   / 1.44 / 54ch
     schedule lede  17px                   / 1.6  / 56ch
     closed state   17px                   / 1.6  / 56ch

   Flattening all seven onto one class is what produced a slow drip of
   one-paragraph corrections. If you add a lede, look up its own values. */
.lede { font-size:clamp(17px,1.9vw,20px); line-height:1.6; color:var(--ink-secondary);
  margin:0; text-wrap:pretty; }

/* ---- Buttons: four styles and only four -----------------------------------
   prominent  flat purple fill, white label   - primary
   tinted     purple at 10%, purple label     - secondary
   glass      floats OVER content
   quiet      label and chevron only          - the low-commitment second path
   No gradients on any button. Hover lightens; press dims to .8 and contracts
   to .96 in 80ms. Fill and label colour never transition - the scroll-driven
   zone flip must land both at once or the label sits dark-on-dark mid-way. */
.btn { position:relative; display:inline-flex; align-items:center; justify-content:center;
  gap:9px; border-radius:999px; white-space:nowrap; text-decoration:none; cursor:pointer;
  border:0; min-height:44px; padding:0 22px; font-size:15px; font-weight:700;
  letter-spacing:-.012em; font-family:var(--font-body);
  transition:box-shadow 200ms ease, opacity 100ms ease, transform 120ms var(--ease); }
.btn:active { transform:scale(.96); opacity:.8; transition-duration:80ms; }
.btn--lg { min-height:56px; padding:0 32px; font-size:17px; }
.btn--prominent { background-color:var(--purple); color:#fff; box-shadow:0 1px 2px rgba(36,0,74,.2); }
.btn--prominent:hover { background-color:#46108c; color:#fff; }
/* The hero CTA is Old Gold, not signal gold: on a pale lavender sheet #F7B910
   reads as a warning where #EFD9A2 reads as brass. 7.6:1. */
.btn--hero { background-color:var(--gold-pale); color:var(--purple-deep);
  box-shadow:0 10px 26px -14px rgba(96,66,10,.4), inset 0 1px 0 rgba(255,255,255,.5); }
.btn--hero:hover { background-color:#F7E8C8; color:var(--purple-deep); }
.btn--tinted { background-color:rgba(51,0,102,.10); color:var(--purple); }
.btn--tinted:hover { background-color:rgba(51,0,102,.17); color:var(--purple); }
/* The .hero .btn--tinted surface treatment that used to sit here is gone with
   the button it existed for. The narrow-screen hero carried a second, tinted
   "See the schedule" control over the crest watermark and needed a real
   surface to stay readable on it; the hero is a single primary now, so the
   rule had no subject left. Same for the dusk variant below.
   The base .btn--tinted pair above stays as part of the button system. */
.btn--gold { font-family:var(--font-label); font-size:13.5px; letter-spacing:.2em;
  padding:0 34px; min-height:56px; background-color:var(--purple); color:#fff; }
/* On the dusk half, prominent inverts to flat gold. */
[data-zone="dusk"] .btn--prominent, [data-zone="dusk"] .btn--gold { background-color:var(--gold-old); color:var(--purple-night); box-shadow:none; }
[data-zone="dusk"] .btn--prominent:hover, [data-zone="dusk"] .btn--gold:hover { background-color:var(--gold-pale); color:var(--purple-night); }
.quiet { display:inline-flex; align-items:center; gap:6px; font-size:17px; line-height:1;
  letter-spacing:-.01em; color:var(--purple); background:none; border:0; padding:8px 2px;
  cursor:pointer; transition:opacity .28s var(--ease); }
.quiet:hover { opacity:.62; }
.quiet i { font-style:normal; font-size:19px; transition:transform .28s var(--ease); }
.quiet:hover i { transform:translateX(4px); }
[data-zone="dusk"] .quiet { color:var(--gold-pale); }
@media (pointer:coarse) { .btn { min-height:var(--hit); } }

/* ---- Cards ---------------------------------------------------------------
   An opaque card in the page flow is its OWN surface - white wherever it
   lands - so no zone rule may reach inside it. */
.card { border-radius:32px; padding:34px 36px; background:#fff; border:1px solid #E3E0EA;
  box-shadow:0 2px 10px rgba(36,0,74,.06); }
.card--accent { border-top:4px solid var(--gold-signal); }
.card h1,.card h2,.card h3,.card h4 { color:var(--ink); }
.card p { color:var(--ink-secondary); }
.lift { transition:transform 350ms ease-out, box-shadow 350ms ease-out, border-color 350ms ease-out; }
.lift:hover { transform:translateY(-3px);
  box-shadow:0 22px 44px -16px rgba(0,0,0,.10), 0 0 22px -4px rgba(227,193,91,.35), 0 0 1px 1px rgba(255,255,255,1) inset;
  border-color:rgba(227,193,91,.45); }
.glass { border-radius:32px; background:var(--glass-surface);
  -webkit-backdrop-filter:var(--glass-blur); backdrop-filter:var(--glass-blur);
  border:1px solid rgba(255,255,255,.6);
  box-shadow:0 20px 40px -15px rgba(0,0,0,.07), inset 0 1px 0 rgba(255,255,255,.7); }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) {
  .glass { background:rgba(255,255,255,.94); }
}
/* NO BORDERED CIRCLE. "Symbols: no bordered circles. The card already is the
   container." The glyph sits on the surface at 30px with a slightly heavier
   stroke, and nothing frames it. This was a 52px frosted disc - which is
   exactly the thing the design deleted, and why the icons read as wrong
   rather than missing. The numeral badge on the step cards is a different
   element (.step__n) and keeps its circle. */
.badge { width:auto; height:auto; display:block; border:0; border-radius:0;
  background:none; box-shadow:none; color:var(--purple-deep); }
.badge svg { width:30px; height:30px; stroke-width:1.45; }
.lift:hover .badge { transform:none; background:none; }

/* ---- Zones: the dusk half of the gradient -------------------------------
   THERE IS ONE ZONE SIGNAL AND IT IS COMPUTED: data-zone="dusk", written by
   layoutSheet() onto whichever [data-zoneable] blocks the sheet has actually
   turned dark. The base ink rules live in the MOTION CONTRACT at the bottom
   of this file, next to data-ink.

   A `.zone` CLASS used to exist alongside it, hardcoded onto #brothers and
   the footer in index.html. Two signals for one fact: they agreed only while
   the page stayed exactly as long as it was, and the page changes length by
   eight events the moment recruitment closes. The class is gone. Never put a
   zone on a section by hand - the whole point of layoutSheet() is that the
   crossover snaps to a real block boundary, so no section can straddle it. */

/* ---- Masthead -----------------------------------------------------------
   FIXED, not sticky, and it owns the progress bar. At rest it is completely
   transparent - no glass, no border - and only materialises once the page has
   moved. The dark variant tracks CUT, the same computed crossover the sheet
   and the zones use, so the bar and the canvas can never disagree. */
.masthead { position:fixed; top:0; left:0; right:0; z-index:140;
  background:transparent; border-bottom:1px solid transparent;
  transition:background 400ms var(--ease), border-color 400ms var(--ease),
             box-shadow 400ms var(--ease); }
.masthead[data-lifted="true"] { background:rgba(255,255,255,.68);
  -webkit-backdrop-filter:blur(24px) saturate(180%); backdrop-filter:blur(24px) saturate(180%);
  border-bottom-color:rgba(0,0,0,.08);
  box-shadow:0 1px 24px -8px rgba(36,0,74,.16); }
.masthead[data-dark="true"][data-lifted="true"] { background:rgba(12,0,26,.76);
  border-bottom-color:rgba(255,255,255,.13); }
/* Variable blur: the glass stays uniform and the blur falls off at the lower
   edge instead of cutting. */
.masthead::after { content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  -webkit-backdrop-filter:blur(16px); backdrop-filter:blur(16px);
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 54%,rgba(0,0,0,.45) 80%,transparent 100%);
  mask-image:linear-gradient(180deg,#000 0%,#000 54%,rgba(0,0,0,.45) 80%,transparent 100%);
  opacity:0; transition:opacity 420ms var(--ease); }
.masthead[data-lifted="true"]::after { opacity:1; }
.masthead__in { max-width:1280px; margin:0 auto; min-height:56px;
  padding:0 max(var(--gutter),env(safe-area-inset-right)) 0 max(var(--gutter),env(safe-area-inset-left));
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:20px; }

.brand { display:flex; align-items:center; gap:11px; justify-self:start; min-height:var(--hit); }
.brand__crest { position:relative; display:block; height:38px; flex:0 0 auto; }
.brand__crest img { height:38px; width:auto; display:block;
  transition:opacity .45s var(--ease); }
.brand__crest--dark { position:absolute; left:0; top:0; opacity:0; }
.masthead[data-dark="true"] .brand__crest--light { opacity:0; }
.masthead[data-dark="true"] .brand__crest--dark { opacity:1; }
/* The lockup is the Greek letters, not the words. */
.brand__lock { display:inline-flex; align-items:baseline; gap:.3em;
  font-family:var(--font-display); font-weight:400; font-size:21px; line-height:1;
  letter-spacing:.07em; white-space:nowrap; }
.visually-hidden { position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }

/* ── The lockup metals. Gold on the left, royal purple on the right, each a
   text-clipped ramp rather than a flat fill.

   THESE RULES ARE LOAD-BEARING AND EASY TO LOSE. They lived inside the old
   masthead block, and rewriting that block dropped them - at which point both
   halves inherited `a { color:var(--purple) }` and the lockup rendered as two
   purple words. If ΔΣΠ is not gold, this is the first thing to check.

   The light-half golds are dark on purpose. At 21px the mark is under the 24px
   large-text line, so it needs 4.5:1, not 3:1 - #85590A measures 5.71:1 on the
   near-white bar where the brighter #C9992F manages only 2.35. The bright
   metals are correct only once the bar goes dark. ── */
.navgold {
  background:linear-gradient(100deg,#5A3800 0%,#744903 26%,#85590A 52%,#744903 78%,#5A3800 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent; }
.navplum {
  background:linear-gradient(100deg,#2E0060 0%,#42008A 26%,#5A1AAE 52%,#42008A 78%,#2E0060 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent; }
.navrule { color:rgba(74,26,134,.3); }
.masthead[data-dark="true"] .navgold {
  background:linear-gradient(100deg,#C9992F 0%,#FBEBB4 30%,#DFB44A 60%,#EFD9A2 100%);
  -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }
.masthead[data-dark="true"] .navplum {
  background:linear-gradient(100deg,#B79BEC 0%,#E2D3FA 30%,#9B7BDB 60%,#C7B0F2 100%);
  -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; }
.masthead[data-dark="true"] .navrule { color:rgba(255,255,255,.3); }

.nav { display:flex; align-items:center; justify-content:center; gap:4px;
  justify-self:center; overflow-x:auto; scrollbar-width:none; }
.nav::-webkit-scrollbar { display:none; }
.nav a { display:inline-flex; flex-direction:column; align-items:center; justify-content:center;
  min-height:36px; padding:0 12px; font-size:14.5px; color:var(--ink-muted);
  transition:color .5s var(--ease); }
.nav a u { display:block; height:2px; width:100%; border-radius:2px; background:var(--purple);
  transform:scaleX(0); transform-origin:center; transition:transform .45s var(--ease);
  margin-top:5px; }
.nav a[aria-current="true"] { color:var(--purple); }
.nav a[aria-current="true"] u { transform:scaleX(1); }
.masthead[data-dark="true"] .nav a { color:rgba(255,255,255,.56); }
.masthead[data-dark="true"] .nav a[aria-current="true"] { color:#fff; }
.masthead[data-dark="true"] .nav a u { background:var(--gold-signal); }

.navtrail { display:flex; align-items:center; justify-content:flex-end; gap:4px; justify-self:end; }
.navcta { display:inline-flex; align-items:center; min-height:36px; padding:8px 6px;
  font-size:14px; font-weight:700; letter-spacing:-.012em; white-space:nowrap;
  color:var(--purple); }
.navcta:hover { color:var(--purple-mid); }
.masthead[data-dark="true"] .navcta { color:var(--gold-signal); }

.menubtn { display:none; width:var(--hit); height:var(--hit); margin-right:-10px;
  border:0; background:transparent; padding:0; cursor:pointer;
  align-items:center; justify-content:center; color:var(--purple); }
.masthead[data-dark="true"] .menubtn { color:#fff; }
.burger { display:block; position:relative; width:20px; height:14px; }
.burger i { position:absolute; left:0; display:block; width:100%; height:2px;
  border-radius:2px; background:currentColor;
  transition:transform 420ms var(--ease), opacity 200ms ease; }
.burger i:nth-child(1) { top:0; }
.burger i:nth-child(2) { top:6px; }
.burger i:nth-child(3) { top:12px; }
.menubtn[aria-expanded="true"] .burger i:nth-child(1) { transform:translateY(6px) rotate(45deg); }
.menubtn[aria-expanded="true"] .burger i:nth-child(2) { opacity:0; }
.menubtn[aria-expanded="true"] .burger i:nth-child(3) { transform:translateY(-6px) rotate(-45deg); }

.menusheet { display:none; flex-direction:column; padding:6px 18px max(18px,env(safe-area-inset-bottom));
  background:var(--glass-surface);
  -webkit-backdrop-filter:var(--glass-blur); backdrop-filter:var(--glass-blur);
  border-top:1px solid rgba(36,0,74,.09);
  box-shadow:0 24px 40px -20px rgba(36,0,74,.35); }
.menusheet a { display:flex; align-items:center; min-height:52px; padding:0 6px;
  font-family:var(--font-label); font-size:19px; letter-spacing:.06em;
  color:var(--purple); border-bottom:1px solid rgba(36,0,74,.07); }
.menusheet a[aria-current="true"] { color:var(--gold-deep); }
.masthead[data-dark="true"] .menusheet { background:rgba(12,0,26,.9); border-top-color:rgba(255,255,255,.13); }
.masthead[data-dark="true"] .menusheet a { color:rgba(255,255,255,.72); border-bottom-color:rgba(255,255,255,.09); }
.masthead[data-dark="true"] .menusheet a[aria-current="true"] { color:var(--gold-signal); }

.progresstrack { height:2px; background:transparent; }

@media (max-width:900px) {
  .masthead__in { grid-template-columns:1fr auto auto; gap:12px; padding:0 18px; }
  .nav { display:none; }
  .menubtn { display:inline-flex; }
  .navcta { margin-left:auto; }
  .masthead[data-open="true"] .menusheet { display:flex; }
}
@media (prefers-reduced-motion:reduce) {
  .brand__crest img, .nav a u, .burger i, .masthead, .masthead::after { transition:none !important; }
}

/* ---- Banner --------------------------------------------------------------
   BELOW the nav, not above it, and fixed like the nav so it is present at every
   scroll position. That makes it the loudest dated claim on the site, which is
   why it swaps copy rather than hiding: hiding it would leave main's 90px
   reserving space for nothing, and off-season there is still one true thing to
   say. It tracks the same data-dark signal the nav does. */
#banner { text-align:center; font-family:var(--font-body); font-size:13px; font-weight:700; letter-spacing:.005em;
  line-height:1.45; padding:8px 20px;
  background:linear-gradient(180deg,#F1EAFF 0%,#E3D8FA 100%);
  color:var(--purple-deep);
  border-bottom:1px solid rgba(255,255,255,.5);
  transition:background 400ms ease, color 400ms ease, border-bottom-color 400ms ease; }
#banner[data-dark="true"] {
  background:linear-gradient(180deg,rgba(59,27,110,.92) 0%,rgba(42,20,72,.92) 100%);
  color:#FFF3D2; border-bottom-color:rgba(227,193,91,.28);
  -webkit-backdrop-filter:blur(18px) saturate(180%); backdrop-filter:blur(18px) saturate(180%); }
#banner:empty { display:none; }
/* main reserves the chrome. The height is written by site.js from the bar's
   own measurement - the banner wraps to two lines on a narrow screen, and the
   design's flat 90px left the first section under it when it did. */
#main { padding-top:90px; }
@media (prefers-reduced-motion:reduce) { #banner { transition:none; } }

/* ---- Hero ---------------------------------------------------------------- */
.hero { position:relative; padding:104px 0 92px; overflow:hidden;
  /* Its own stacking context, so the watermark cannot escape the section. */
  isolation:isolate;
  /* NO BASE COLOUR, and that is the point - the dawn sheet composites through,
     exactly as .hero__mesh does on the pinned hero. This used to end with an
     opaque `linear-gradient(180deg,#fff,#fcfbff)`, and that flat white was
     what made this hero read dead in its lower half: the two radials above
     both sit in the top quarter and reach transparent at 60-62% of their ray,
     so below about 57% of the hero's height nothing was left but the base.
     Measured at 390x553 with the content hidden, the bottom two rows of a 7x4
     sample grid came back IDENTICAL at every x - #FDFCFF then #FCFBFF, chroma
     3 and 4 - while the sheet underneath was carrying lavender at chroma 21-27
     that the base was painting over.
     Two radials is still fewer than the pinned hero's four; the blue and rose
     that cover ITS lower half are deliberately not here yet. */
  background:radial-gradient(56% 50% at 22% 26%, rgba(178,150,236,.44) 0%, rgba(178,150,236,0) 62%),
             radial-gradient(50% 46% at 82% 22%, rgba(247,201,104,.38) 0%, rgba(247,201,104,0) 60%); }
/* TOP-ANCHORED, NOT CENTRED - and the reason is not the one it looks like.
   The desktop hero does NOT give its crest empty space. Measured at 1280x900:
   the crest spans 138.6-514.9, the wordmark sits at 230-256.9 and the kicker at
   488.9-505.9, so BOTH of those sit on top of the mark. The only thing that
   clears it is the headline, which starts at 535.9 - 21px below the crest's
   bottom edge.

   So the rule being ported is "the headline clears the crest", not "the crest
   gets its own band". Centring the mark in the frame put the headline 100%
   inside it at 390px (136.6px of overlap) along with the whole lede.

   place-items:start centre, so the mark hangs from the top of the hero behind
   the wordmark and kicker, and the headline begins below it. */
/* ⚠️ PADDING-TOP TRADES 1:1 AGAINST HEADLINE CLEARANCE. Every pixel added
   here pushes the mark down and takes a pixel off the gap between the crest's
   bottom and the headline's top. The budget is whatever the tightest width has
   - 360px, which is the worst case, not 320. Measured across 320-880:

       padding-top   gap below chrome   worst clearance (at 360px)
            6px            6px               +16
           14px           14px                +8     <- current
           18px           18px                +4
           22px           22px                 0     <- fails

   THE CREST WAS NEVER CLIPPED. It was reported as cut off at the hero's upper
   edge; it is not - at 6px it rendered whole, 6.3px below the fixed chrome, and
   simply read as cramped against the banner. So padding here buys breathing
   room, not a recovered crown, and there is no amount that is "required".

   14px was chosen as the middle: it more than doubles the gap while keeping
   clearance at +8, still twice the +4 that was rejected when the mark was
   sized. If more gap is wanted, the way to get it is NOT more padding - shrink
   the mark to min(100px,30vw), which frees about 12px of height and would allow
   18px of padding at the original +16 clearance. */
.hero__crest { position:absolute; inset:0; display:grid; place-items:start center;
  padding-top:14px; pointer-events:none; }
/* THE WATERMARK IS TEXTURE, NOT AN ELEMENT, and min(520px,92vw) only holds
   that at the wide end of this hero's range. The static hero runs from about
   320px to 900px, and the two arms of the min() behave completely differently
   across it: at 900px the 520px cap wins and the crest is 58% of the hero's
   width, but at 390px the 92vw arm wins and it is 92%. Measured on a 390x844
   phone against the desktop hero's own crest:

       narrow  .hero__crest img    359x439 in a 390x510 hero   86.3% of height
       desktop #heroMark img       307x376 in a 1280x900 pane  41.8% of height

   Twice the proportional height and nearly four times the width, and the CTA
   row sits directly on the densest part of the shield.

   The old rule was wrong at BOTH ends, not just on a phone. At 760px it
   resolved to a crest 637px tall inside a 481px hero - 132% of the frame,
   overflowing it - because the hero's height moves the OPPOSITE way to its
   width: widen the viewport, the headline stops wrapping, the hero gets
   shorter, and a 520px cap keeps the mark the same size.

   (An earlier version of this comment claimed the 520px cap "already keeps it
   in proportion" at tablet width and that there was nothing to correct up
   there. That was wrong. It was never measured above 640px - it was assumed
   from the phone measurement. 760px is where it is worst.)

   ⚠️ DO NOT REWRITE THIS AS A PERCENTAGE OF THE HERO. `height:min(Xpx,56%)`
   is the obvious fix and it does not work here: the image is a GRID ITEM
   (`.hero__crest` is `display:grid; place-items:center`), so a percentage
   height resolves against its grid ROW, and with no explicit rows that row is
   sized to its contents - this image. Circular, so it resolves to garbage.
   It is NOT that the hero lacks a definite height: the hero computes
   `min-height:0px` and the wrapper is `position:absolute; inset:0` at a
   definite 481.375px. Switch that wrapper to `display:block` and the same
   `height:56%` resolves correctly to 269.562px. The grid row is the problem,
   nothing else.

   So: sized by WIDTH, with a flat cap, and no percentage anywhere.
   Not vh either - the hero's height is content-driven and does NOT track the
   viewport. Measured 466-510px of hero against viewports from 640 to 1024,
   and a 390x844 phone and a 390x640 landscape phone produce an identical
   190x233 crest. A vh-sized mark would shrink to nothing in landscape while
   the frame it sits in stayed the same size.
   A flat cap works because the hero's height is near-constant across the whole
   320-880px range this hero serves, so one cap holds one proportion. It also
   fails safe - if the copy grows and the hero gets taller, the mark gets
   proportionally smaller and can never overflow.

   ⚠️ TWO ALTERNATIVES WERE MEASURED ACROSS 320-880 AND BOTH FAIL. Do not
   re-derive them:

     - 140px top-anchored overlaps the headline at EVERY width, -4 to -20px.
       140 looks like the safe middle and is not.
     - Centring at ANY size leaves the headline 102-147px inside the mark.
       Size was never the problem; position was.

   110px was chosen over 120px deliberately. 120 cleared, but by only +4px at
   360-390, and one extra headline line would have eaten it - that copy has
   changed repeatedly. 110 buys about 12px more everywhere.

   The mark is now ~26-30% of hero height against the pinned hero's 41.8%. That
   is a real change in character - a crest above the copy rather than a texture
   behind it - and it was accepted knowingly.

   COSTS NOTHING IN LAYOUT. The wrapper is position:absolute, so moving or
   resizing this mark does not change hero height or fold clearance by a single
   pixel at any width. Verified. */
.hero__crest img { width:min(110px,32vw); opacity:.10; }
[hidden] { display:none !important; }
.hero__in { position:relative; z-index:2; max-width:860px; margin:0 auto; text-align:center; }
/* --gold-darkest, not --gold-deep. Removing the hero's opaque white base let
   the dawn sheet's lavender through, and #8f6804 on that ground measured
   4.20:1 against a 4.5 bar - an AA regression the base had been hiding. The
   pinned hero's own kicker already uses --gold-darkest for the same reason:
   it has never had a white base under it. 6.68:1 on the worst ground here. */
.hero__kicker { font-family:var(--font-label); font-size:15px; letter-spacing:.34em;
  color:var(--gold-darkest); margin:0 0 10px; animation:fadeUp .8s var(--ease) both; }
/* WEIGHT 400, AND THE RULE IN CLAUDE.md WAS AMENDED FIRST - see the Georgia
   note there. 700 was written when this headline was "Delta Omicron.", a
   two-word nameplate. It is now the same 84-character sentence as the pinned
   hero's beat 1, whose h2 has always been 400, so the same words were coming
   out bold on a phone and regular on a desktop.
   Only the weight was matched. line-height stays .96 and letter-spacing stays
   -.045em rather than beat 1's 1.1 / -.018em: those are tuned for a wide
   column and read loose at four-plus lines in a 268px one.
   28px/8.5vw, with 24px taken out of the margins below (kicker 18->10,
   subhead 32->22, wordmark 16->10). The margin is the better lever: 24px is
   returned on EVERY device, three times what the next type step down would
   buy, and it costs no legibility. Stepping the type to 26px/8vw instead
   bought 9px.
   THE CLAMP MOVED BOTH ARMS, AND IT HAD TO. Lowering the floor alone does
   nothing below about 35px on a small phone, because at 320px `11vw` is
   35.2px and the vw term takes over - a floor of 1px measures identically to
   a floor of 34px there. 30px/9vw is the loosest pair that puts the CTA above
   the fold on all nine test devices, from +13px on a 320x568 phone up. */
.hero h1 { font-size:clamp(28px,8.5vw,70px); line-height:.96; letter-spacing:-.045em;
  font-weight:400; text-wrap:balance; color:var(--ink); margin:0 0 20px;
  animation:fadeUp .9s var(--ease) .08s both; }
/* The chapter's name, back in the narrow hero. It left when the headline
   became the descriptive sentence, and the masthead's Greek lockup was the
   only thing naming the chapter above the fold on a phone.
   Styled as the pinned hero's own mark - same face, weight, tracking and
   size ramp - but IN FLOW above the kicker rather than absolutely placed,
   because there is no pinned pane here to hang it in. */
.hero__wm { font-family:var(--font-display); font-weight:400;
  font-size:clamp(15px,4.2vw,32px); line-height:1; letter-spacing:.06em;
  white-space:nowrap; opacity:.95; margin:0 0 10px;
  display:flex; justify-content:center; align-items:baseline; gap:.4em; }
.hero__lede { font-size:clamp(18px,4.4vw,23px); line-height:1.34; letter-spacing:-.022em;
  color:var(--ink-secondary); margin:0 0 22px; text-wrap:balance;
  animation:fadeUp .9s var(--ease) .16s both; }
.hero__cta { display:flex; flex-wrap:wrap; gap:12px; justify-content:center;
  animation:fadeUp .9s var(--ease) .24s both; }

/* ---- Impact figures ------------------------------------------------------ */
/* EVEN RHYTHM, NOT EVEN COLUMNS. The auto-fit rule below gives three columns
   of identical WIDTH, which is not the same thing as three numbers evenly
   spaced: the figures are left-aligned and wildly different widths, so the
   whitespace after each one varies with its digit count. Measured at 1280 -
   ink 315 / 167 / 122.5 in 344px columns, leaving visual gaps of 53px and
   201px. Nearly four times as much air on one side as the other.

   So once all three fit on one row, the columns are sized to their CONTENT and
   the leftover space is distributed between them instead. The auto-fit rule
   still governs below that, where the row wraps to two or three lines and
   equal columns are the right answer. */
.impact { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(190px,100%),1fr)); gap:48px 24px; }
@media (min-width:670px) {
  .impact { grid-template-columns:repeat(3,max-content); justify-content:space-between; }
}
.impact__v { font-family:var(--font-display); font-size:clamp(42px,5vw,66px); line-height:1;
  letter-spacing:-.03em; font-weight:700; color:var(--purple); font-variant-numeric:tabular-nums; }
.impact__l { font-size:15px; line-height:1.4; color:var(--ink-secondary); margin-top:12px; text-wrap:pretty; }
[data-zone="dusk"] .impact__v { color:var(--gold-old); }

/* ---- Heritage card ------------------------------------------------------- */
.heritage { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr));
  gap:30px 56px; align-items:start; border-radius:28px; padding:34px 36px 30px; }
.heritage__stats { display:flex; flex-wrap:wrap; gap:12px 34px; }
.heritage__v { font-family:var(--font-label); font-size:27px; line-height:1; color:var(--purple-deep); }
/* Caps are set HERE, not typed into data/chapter.js - see the note above the
   heritage rows in that file for why an all-caps label there fails check.sh. */
.heritage__l { font-family:var(--font-label); font-size:10.5px; letter-spacing:.2em;
  color:var(--gold-darkest); margin-top:5px; text-transform:uppercase; }
.rec__head { display:flex; align-items:baseline; justify-content:space-between; gap:16px;
  flex-wrap:wrap; padding-bottom:12px; border-bottom:1px solid rgba(74,26,134,.16); }
.rec__title { font-family:var(--font-display); font-size:18px; line-height:1.15;
  letter-spacing:-.02em; font-weight:400; color:var(--ink); }
.rec__kind { font-family:var(--font-label); font-size:11px; letter-spacing:.2em; color:var(--gold-deep); }
.rec__row { display:flex; align-items:baseline; justify-content:space-between; gap:18px;
  padding:11px 2px; border-bottom:1px solid rgba(74,26,134,.1); }
.rec__row span:first-child { font-size:14.5px; line-height:1.4; color:var(--ink); }
.rec__yrs { font-family:var(--font-label); font-size:13px; letter-spacing:.12em;
  color:var(--ink-muted); white-space:nowrap; font-variant-numeric:tabular-nums; }

/* ---- Values --------------------------------------------------------------
   Four static peers in one aligned grid: equal widths, one surface, no card
   able to occlude another's copy. auto-fit rather than a media query, so it
   is 4 across at 1180, 2 at tablet and 1 at 320 with no breakpoint to keep in
   step. align-items:stretch levels the row off the tallest lede.

   THE RAMP IS FOUR EVEN STEPS OF ONE PURPLE, and it lives here rather than in
   chapter.js because it is not chapter content. It starts deep enough to hold
   an edge on the pale field so the set reads as ordered. These cards used to
   take a per-value gradient from the data file in four different hues -
   lavender, champagne, blue, rose - which read as four unrelated pastels. */
.values { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(248px,100%),1fr));
  gap:30px; align-items:stretch; }
.vcard { border-radius:32px; padding:32px 28px 34px; gap:18px; display:flex;
  flex-direction:column; justify-content:flex-start; position:relative; height:100%;
  border:1px solid rgba(255,255,255,.75);
  box-shadow:0 20px 44px -18px rgba(36,0,74,.18);
  transition:box-shadow .45s var(--ease), transform .45s var(--ease); }
.vcard:nth-child(1) { background:#F0EAFB; }
.vcard:nth-child(2) { background:#E8DFF7; }
.vcard:nth-child(3) { background:#DFD2F3; }
.vcard:nth-child(4) { background:#D6C6EF; }
.vcard:hover { box-shadow:0 40px 80px -24px rgba(26,21,36,.28), 0 0 1px 1px rgba(255,255,255,.95) inset; }
.vcard h3 { font-size:clamp(21px,2.1vw,28px); line-height:1.04; letter-spacing:-.03em;
  font-weight:700; color:var(--ink); margin:0 0 10px; }
.vcard p { font-size:14px; line-height:1.55; color:var(--ink-secondary); text-wrap:pretty; }
.vcard .badge { margin-bottom:24px; }
/* The ramp is a light surface wherever it lands, so the zone must not reach
   inside it - same rule the opaque cards follow. */
[data-zone="dusk"] .vcard h3 { color:var(--ink); }
[data-zone="dusk"] .vcard p { color:var(--ink-secondary); }

/* ---- Benefits ------------------------------------------------------------ */
/* An editorial list, not a card deck: one left rail, hairline-separated.
   Three columns - glyph, title on a 17ch measure, then the text. */
/* The title column is sized against the h3's OWN scale, not the row's body
   font. `ch` resolves against the element it is declared on - here the row, at
   17px Lato, so 17ch came out around 145px while the h3 renders up to 31px.
   "Scholarships" overran that box. In the design, where these titles are
   gradient-clipped, the overrun was painted transparent and the word came out
   as "Scholarshi"; ours are flat-coloured so it merely wrapped, but the column
   was undersized either way. */
.benefit { display:grid;
  grid-template-columns:56px minmax(0,clamp(210px,25vw,310px)) minmax(0,1fr);
  gap:0 44px; align-items:start;
  padding:46px 0; border-top:1px solid rgba(74,26,134,.16); }
.benefit:last-of-type { border-bottom:1px solid rgba(74,26,134,.16); }
@media (max-width:880px) {
  .benefit { grid-template-columns:1fr; gap:16px 0; padding:38px 0; }
  .benefit h3, .benefit p { grid-column:auto; }
}
.benefit h3 { font-size:clamp(22px,2.5vw,31px); line-height:1.1; letter-spacing:-.03em;
  font-weight:700; color:var(--ink-slate); margin:0; text-wrap:balance; }
.benefit p { font-size:clamp(16px,1.6vw,17.5px); line-height:1.68; color:var(--ink-secondary);
  margin:0; max-width:58ch; text-wrap:pretty; }
[data-zone="dusk"] .benefit { border-top-color:rgba(227,193,91,.2); }

/* ---- The values in practice: rail ---------------------------------------
   The photo IS the card, and the caption sits outside it - nothing is ever
   printed over a picture, so no scrim is needed and no caption can land on a
   face. 3:2 landscape at 600px: every one of these four is a photograph of a
   ROOM (a panel, a service day, a study table, a group), and a portrait crop
   fights that. The tile still crops at the right edge so the next one is
   visibly there, which is what tells a reader there is more without a
   scrollbar. */
.rail { display:flex; gap:22px; overflow-x:auto;
  scroll-snap-type:x mandatory; scroll-behavior:smooth;
  scrollbar-width:none; overscroll-behavior-x:contain;
  --cardw:min(600px,86vw);
  /* This padding is what centres the active card - not a margin, not a
     transform. Change it and the snap points stop lining up. */
  padding:6px max(var(--gutter), calc((100vw - var(--cardw)) / 2)) 0; }
.rail::-webkit-scrollbar { height:0; }
.rail__card { position:relative; flex:0 0 auto; width:var(--cardw);
  display:flex; flex-direction:column; gap:16px; scroll-snap-align:center;
  transition:opacity 650ms var(--ease); }

/* TWO SHELLS, and the outer one is the point. The mat carries the gradient
   and the drift and keeps a 12px margin of itself showing around the
   photograph, permanently, like the mount in a picture frame. Previously the
   ombre was the photo's own background, so the moment a picture landed every
   trace of it was gone. */
.rail__photo { position:relative; aspect-ratio:3 / 2; overflow:hidden; isolation:isolate;
  padding:12px; border-radius:32px;
  border:1px solid rgba(255,255,255,.6);
  background:linear-gradient(168deg,#F3EEFE 0%,#EBE2FA 38%,#F3E6EC 68%,#FAF3E2 100%);
  box-shadow:0 20px 40px -15px rgba(0,0,0,.08);
  transition:transform 650ms var(--ease), box-shadow 650ms var(--ease); }
.rail__photo::before { content:''; position:absolute; inset:-22%; z-index:0; pointer-events:none;
  background:
    radial-gradient(38% 34% at 26% 24%, rgba(140,88,214,.4) 0%, rgba(140,88,214,0) 68%),
    radial-gradient(34% 32% at 78% 68%, rgba(227,193,91,.38) 0%, rgba(227,193,91,0) 70%),
    radial-gradient(30% 28% at 62% 20%, rgba(226,150,190,.28) 0%, rgba(226,150,190,0) 72%);
  animation:hiDrift 22s var(--ease) infinite alternate;
  will-change:transform, opacity; }
@keyframes hiDrift {
  0%   { transform:translate3d(-4%,-3%,0) scale(1);    opacity:.85; }
  50%  { transform:translate3d(3%,4%,0) scale(1.08);   opacity:1; }
  100% { transform:translate3d(5%,-2%,0) scale(1.03);  opacity:.9; }
}
/* Translucent, not opaque: the drift tints through it, so the ombre reads as
   one continuous field across mat and frame. */
.rail__inner { position:relative; z-index:1; height:100%; overflow:hidden;
  border-radius:22px; border:1px solid rgba(255,255,255,.7);
  background:rgba(255,255,255,.62);
  box-shadow:0 2px 12px rgba(36,0,74,.08);
  display:grid; place-items:center; }
.rail__inner img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.rail__ph { font-family:var(--font-label); font-size:12px; letter-spacing:.22em;
  color:var(--gold-darkest); }
/* The centred card lights up and its neighbours stand back. This is the focus
   cue the arrows used to give, moved off the photograph and into the frame. */
.rail__card[data-on="false"] { opacity:.62; }
.rail__card[data-on="false"] .rail__photo { transform:scale(.94); }
.rail__card[data-on="true"] .rail__photo {
  box-shadow:0 30px 56px -18px rgba(92,43,157,.26), 0 0 0 2px rgba(227,193,91,.55); }
.rail__card:hover .rail__photo { transform:translateY(-4px);
  box-shadow:0 32px 60px -20px rgba(92,43,157,.24); }
.rail__cap { padding-left:4px; }
.rail__eyebrow { font-family:var(--font-label); font-size:12px; letter-spacing:.22em;
  text-transform:uppercase; color:var(--gold-deep); margin:0 0 7px; }
[data-zone="dusk"] .rail__eyebrow { color:var(--gold-old); }
/* KEPT BUT NOT RENDERED, exactly as the design has it. Ported from the
   export's [data-hilabel] (DC ~817), which the design styles and never prints -
   the rail shows the value NAME through [data-hieyebrow] above. We rendered the
   caption here for one pass on 10 Aug 2026 and reversed it: on the page the
   sentences read as four unrelated lines instead of the four values.
   The rule stays so the option is one line away and the port is not lost.
   WEIGHT 400, NOT THE DESIGN'S 500. Georgia has 400 and 700 and nothing
   between, so a requested 500 resolves DOWN to 400 - see the Georgia note in
   CLAUDE.md. 400 is what the design actually renders. */
.rail__label { font-family:var(--font-display); font-size:clamp(18px,1.9vw,23px);
  line-height:1.22; letter-spacing:-.022em; font-weight:400; color:#1a1524;
  margin:0; max-width:24ch; text-wrap:pretty; }
[data-zone="dusk"] .rail__label { color:#F5F5F7; }
[data-zone="dusk"] .rail__photo { border-color:rgba(227,193,91,.3); }
[data-zone="dusk"] .rail__inner { border-color:rgba(255,255,255,.5); }
@media (prefers-reduced-motion:reduce) {
  .rail { scroll-behavior:auto; }
  .rail__photo::before { animation:none !important; transform:none !important; }
  .rail__card, .rail__photo { transition:none !important; }
  .rail__card[data-on="false"] { opacity:1 !important; }
  .rail__card[data-on="false"] .rail__photo { transform:none !important; }
  .rail__card:hover .rail__photo { transform:none !important; }
}

/* ---- Rail controls -------------------------------------------------------
   Outside every picture, always. The dots and the play/pause sit in a pill
   below the rail rather than floating on the photographs. */
.railpill { display:flex; align-items:center; gap:14px; width:max-content;
  margin:28px auto 0; padding:8px 16px; border-radius:999px;
  background:#EDEAF3; border:1px solid #E3E0EA; }
.raildots { display:flex; align-items:center; gap:0; }
/* THE BUTTON NEVER PAINTS. It is a transparent hit target; the visible dot is
   its .dottrack child, sized explicitly. Painting the button and clipping its
   padding away is fragile - `background` is a shorthand, so any later
   background rule resets background-clip and the whole padded box appears as
   a grey slab. Sizing is layout, not padding, so flex guarantees no two
   targets overlap. */
.raildot { flex:0 0 auto; box-sizing:border-box; width:14px; height:36px;
  padding:0; border:0; background:none; display:grid; place-items:center;
  cursor:pointer; transition:width .5s var(--ease); }
.raildot[data-on="true"] { width:42px; }
.raildot:focus-visible { outline:3px solid var(--gold-signal); outline-offset:0; border-radius:7px; }
.dottrack { display:block; width:6px; height:6px; border-radius:999px; overflow:hidden;
  background:#7A7484; pointer-events:none;
  transition:width .5s var(--ease), border-radius .5s var(--ease), background .5s var(--ease); }
.raildot[data-on="true"] .dottrack { width:34px; border-radius:4px; background:rgba(26,21,36,.2); }
/* The active dot is a COUNTDOWN, not just a marker: the fill crosses it over
   exactly the interval autoplay waits, so the wait is legible instead of
   arbitrary. Pure CSS - it starts because the [data-on] rule begins applying.
   THE 5s HERE AND THE INTERVAL IN site.js MUST STAY IN STEP. */
.dotfill { display:block; height:100%; width:0; background:var(--ink); }
.raildot[data-on="true"] .dotfill { animation:dotFill 5s linear forwards; }
@keyframes dotFill { from { width:0; } to { width:100%; } }
/* Paused or hand-driven, the bar fills solid rather than freezing part-way -
   a stalled half-bar promises a countdown that is not running. */
.raildots[data-paused="true"] .raildot[data-on="true"] .dotfill { animation:none; width:100%; }
.railplay { position:relative; width:30px; height:30px; flex:0 0 auto; border-radius:50%;
  display:grid; place-items:center; cursor:pointer;
  border:1px solid rgba(29,29,31,.1); background:rgba(255,255,255,.7); color:var(--ink-slate); }
.railplay svg { width:14px; height:14px; display:block; }
.railplay::after { content:""; position:absolute; inset:-8px; border-radius:50%; }
.railplay:hover { transform:scale(1.1); border-color:rgba(143,104,4,.4); }
.railplay:active { transform:scale(.96); }
.railplay:focus-visible { outline:3px solid var(--gold-signal); outline-offset:3px; }
[data-zone="dusk"] .railpill { background:rgba(29,11,56,.72); border-color:rgba(227,193,91,.22); }
[data-zone="dusk"] .railplay { border-color:rgba(255,255,255,.28); }
[data-zone="dusk"] .dottrack { background:rgba(255,255,255,.28); }
[data-zone="dusk"] .raildot[data-on="true"] .dottrack { background:rgba(255,255,255,.18); }
[data-zone="dusk"] .dotfill { background:var(--gold-old); }
@media (pointer:coarse) {
  .raildot { width:var(--hit); height:var(--hit); }
  .raildot[data-on="true"] { width:72px; }
}
@media (prefers-reduced-motion:reduce) {
  .raildot, .dottrack { transition:none !important; }
  .raildot[data-on="true"] .dotfill { animation:none !important; width:100% !important; }
}

/* ---- Steps ---------------------------------------------------------------
   The step cards ARE the meter: each fills to its own share of the way in, so
   card 1 fills a quarter and card 4 fills entirely. Only card 4 goes gold, so
   "completely filled" reads as the payoff. grid-auto-rows:1fr keeps the four
   the same height whatever the copy does. */
.steps { display:grid; grid-auto-rows:1fr; gap:14px; }
.step { position:relative; overflow:hidden; display:grid; grid-template-columns:56px 1fr;
  gap:24px; align-items:start; border-radius:26px; padding:30px 32px; cursor:default;
  transition:transform 300ms var(--ease), box-shadow 300ms ease, border-color 300ms ease; }
.step__fill { position:absolute; left:0; top:0; bottom:0; width:0; z-index:0;
  pointer-events:none; opacity:0;
  transition:width 850ms var(--ease), opacity 400ms ease; }
.step__n, .step__body { position:relative; z-index:2; }
.step__n { width:52px; height:52px; border-radius:50%; display:grid; place-items:center;
  background:#F3F0F8; border:1px solid #E7E2F0; font-family:var(--font-label);
  font-size:22px; color:var(--purple-deep);
  transition:background 300ms ease, color 300ms ease, border-color 300ms ease, transform 300ms var(--ease); }
.step h4 { font-family:var(--font-display); font-size:21px; line-height:1.16;
  letter-spacing:-.02em; font-weight:400; color:var(--ink); margin:0 0 8px; }
.step p { font-size:15.5px; line-height:1.66; color:var(--ink-secondary); max-width:64ch; text-wrap:pretty; }
.step[data-on="1"] { border-color:rgba(246,196,69,.6);
  box-shadow:0 28px 56px -18px rgba(92,43,157,.26), 0 0 0 1px rgba(246,196,69,.32) inset;
  transform:translateY(-3px); }
.step[data-on="1"] .step__fill { opacity:1; }
/* THE ACTIVE BADGE ONLY SCALES. It does not go gold, and it does not take a
   ring - it keeps the pale lavender fill and the purple numeral it has at rest.

   The design does carry a rule that would fill it with --gold-metallic, but
   that rule never applies: the badge's fill, border and numeral colour are all
   set INLINE on the element, and an inline declaration beats a non-important
   selector. Only `transform` is absent inline, so the scale is the one part
   that lands. Reading the rule without checking what it competes with is how
   this shipped gold twice. */
.step[data-on="1"] .step__n { transform:scale(1.08); }
.step:focus-visible { outline:2px solid var(--gold-deep); outline-offset:3px; }
@media (prefers-reduced-motion:reduce) {
  .step, .step__fill, .step__n { transition:none !important; }
  .step[data-on="1"] { transform:none; }
  .step[data-on="1"] .step__n { transform:none; }
}

/* ---- Countdown ticker ---------------------------------------------------- */
.ticker { display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:6px 10px; padding:16px 26px; margin:0 0 24px; border-radius:999px;
  background:#fff; border:1px solid #E3E0EA; box-shadow:0 2px 10px rgba(36,0,74,.06);
  animation:tickerUnpack 750ms var(--ease) both; }
.tick { display:flex; align-items:baseline; gap:8px; padding:0 14px; position:relative; }
.tick + .tick::before { content:""; position:absolute; left:0; top:12%; bottom:12%; width:1px;
  background:linear-gradient(180deg,rgba(107,74,2,0),rgba(107,74,2,.3),rgba(107,74,2,0)); }
.tick__v { font-family:var(--font-display); font-weight:700; font-size:clamp(26px,3vw,40px);
  line-height:1; letter-spacing:-.02em; color:var(--purple-deep); font-variant-numeric:tabular-nums; }
.tick__l { font-family:var(--font-label); font-size:11px; letter-spacing:.24em; color:var(--gold-darkest); }
.tick__note { flex:0 1 auto; font-size:14px; line-height:1.5; color:var(--gold-darkest); margin:0; }
@media (max-width:700px) { .ticker { border-radius:32px; } .tick__note { text-align:center; padding:6px 0 0; } }

/* ---- Crest separator ----------------------------------------------------- */
.crestbreak { display:flex; justify-content:center; margin:22px 0 54px; pointer-events:none; }
.crestbreak img { width:clamp(86px,9vw,124px); height:auto; opacity:0;
  transform:translateY(-32px) scale(1.06);
  transition:opacity 850ms var(--ease), transform 850ms cubic-bezier(.34,1.4,.5,1); }
/* frameLoop() stamps data-settled="1" at 88% of the viewport. This rule used
   to key off `.is-in`, which came from reveals() - dead code - so the crest
   under the countdown sat at opacity 0 permanently. */
.crestbreak[data-settled="1"] img { opacity:.95; transform:none; }
@media (prefers-reduced-motion:reduce) { .crestbreak img { opacity:.95; transform:none; transition:none; } }

/* ---- Segmented controls -------------------------------------------------
   Used twice: over the schedule, and over the five officer groups. Two to
   five mutually exclusive options, which is the whole range this control is
   good for. */
.segwrap { display:flex; margin-bottom:44px; max-width:100%; }
.seg { display:flex; max-width:100%; overflow-x:auto; scrollbar-width:none;
  background:#EDEAF3; border:1px solid rgba(255,255,255,.6); border-radius:999px; padding:5px;
  box-shadow:0 20px 40px -15px rgba(0,0,0,.05), inset 0 2px 4px rgba(36,0,74,.05); }
.seg::-webkit-scrollbar { height:0; }
/* The ink is a per-zone custom property, not a fixed colour - the same control
   sits on a pale bar at the top of the page and on dark glass at the bottom.
   --tab-loz is the sliding selection; on the dusk half it goes gold, the same
   inversion every other prominent fill makes. */
:root { --tab-ink-off:var(--ink-muted); --tab-ink-on:var(--purple-deep); --tab-loz:#fff; }
[data-zone="dusk"] { --tab-ink-off:rgba(255,255,255,.82); --tab-ink-on:#1D0B38; --tab-loz:var(--gold-old); }
[data-ink="deep"] { --tab-ink-off:#3A3348; }
.card, .purposecard { --tab-ink-off:var(--ink-secondary); --tab-ink-on:#fff; --tab-loz:#55007d; }
.seg button { border:0; background:transparent; border-radius:999px; padding:0 18px;
  min-height:36px; font-size:13px; font-weight:700; letter-spacing:.02em; color:var(--tab-ink-off);
  cursor:pointer; white-space:nowrap; transition:background .3s var(--ease), color .3s var(--ease); }
.seg button[aria-selected="true"] { background:var(--tab-loz); color:var(--tab-ink-on);
  box-shadow:0 2px 8px rgba(36,0,74,.10); }
[data-zone="dusk"] .seg { background:rgba(29,11,56,.72); border-color:rgba(227,193,91,.22); }
@media (pointer:coarse) { .seg button { min-height:var(--hit); } }
.evrow { display:grid; grid-template-columns:minmax(80px,104px) 1fr; gap:28px; padding:28px 0;
  border-top:1px solid rgba(74,26,134,.16); align-items:start; }
.evrow__d { font-family:var(--font-label); font-size:38px; line-height:1; color:var(--purple-deep); }
/* The month is an EYEBROW and takes the eyebrow ladder, not a colour of its
   own. It is written #6B4A02 inline in the design, but the global eyebrow rule
   overrides that to chapter purple - so in a row the day and the month are
   both purple, and in the tabling card both go gold together. Giving this its
   own gold left the rows reading purple-then-gold down the date column while
   the card above read gold-then-gold. */
.evrow__m { font-family:var(--font-label); font-size:12.5px; letter-spacing:.22em;
  color:var(--purple); margin-top:4px; }
.evrow h4 { font-family:var(--font-display); font-size:19px; line-height:1.2; letter-spacing:-.02em;
  font-weight:400; color:var(--ink); margin:0 0 6px; }
.evrow__meta { font-size:14px; color:var(--ink-muted); margin:0 0 10px; }
.evrow p { font-size:15.5px; line-height:1.66; color:var(--ink-secondary); max-width:62ch; text-wrap:pretty; }
[data-zone="dusk"] .evrow { border-top-color:rgba(227,193,91,.2); }
[data-zone="dusk"] .evrow__d { color:var(--gold-old); }

/* ---- FAQ ----------------------------------------------------------------- */
.faqgroup { margin-bottom:54px; }
.faqgroup > p { font-family:var(--font-label); font-size:13px; letter-spacing:.3em;
  color:var(--purple); margin:0 0 14px; }
.faq { border-top:1px solid rgba(74,26,134,.16); }
.faq details { border-bottom:1px solid rgba(74,26,134,.16); }
/* Lato, not Georgia. The design sets no font-family here, so the question
   inherits body type - 17.5px/700 at -.02em. Georgia at this weight read as a
   headline and made nine of them look like nine sections. */
.faq summary { display:flex; align-items:center; justify-content:space-between; gap:24px;
  padding:22px 4px; cursor:pointer; list-style:none; font-family:var(--font-body);
  font-size:17.5px; line-height:1.35; letter-spacing:-.02em;
  font-weight:700; color:var(--ink); min-height:52px; }
.faq summary::-webkit-details-marker { display:none; }
.faq summary svg { width:19px; height:19px; color:var(--gold-deep); flex:0 0 auto; transition:transform .3s var(--ease); }
.faq details[open] summary svg { transform:rotate(180deg); }
.faq p { font-size:16px; line-height:1.74; color:var(--ink-secondary);
  margin:0; padding:0 4px 28px; max-width:66ch; text-wrap:pretty; }
[data-zone="dusk"] .faqgroup > p { color:var(--gold-old); }
[data-zone="dusk"] .faq, [data-zone="dusk"] .faq details { border-color:rgba(227,193,91,.2); }

/* ---- The purpose card ----------------------------------------------------
   A light sheet in both zones, like the opaque cards: it sits ON the canvas
   rather than belonging to it. The zone protection is at the bottom of this
   file with the other own-surface rules. */
.purposecard { border:1px solid #ECEAF2; border-radius:34px; padding:56px 50px;
  background:linear-gradient(150deg,#F8F5FD 0%,#FCF6F8 55%,#FDFBF3 100%); margin-bottom:80px; }
/* Full ink, not the secondary grey the other card copy uses - this is a
   quoted statement at display scale, not supporting text. The colour itself
   is set with the other own-surface rules at the bottom of this file. */
.purposecard__q { font-size:clamp(18px,2.2vw,26px); line-height:1.52; letter-spacing:-.022em;
  margin:0; max-width:58ch; text-wrap:pretty; }
@media (max-width:640px) { .purposecard { padding:36px 26px; } }

/* ---- Roster --------------------------------------------------------------
   auto-FILL, not auto-fit. auto-fit collapses the tracks it does not fill and
   `1fr` hands that space to the survivors, so a two-card panel stretched each
   card to nearly double the executive size - the exact opposite of "smaller,
   all the same". Lowering the minimum cannot fix that; it caps nothing.
   auto-fill keeps the empty tracks, so a short panel renders at the intended
   width and leaves the rest of the row empty. The width itself comes from the
   active tab: 244px for the board, 196px for chairs and the other rolls,
   written onto the grid by site.js. A committee seat is not a vice
   presidency. */
/* ⚠️ THE 244px BELOW IS A FALLBACK. IT IS NOT WHAT PAINTS.
   renderRoster() in site.js writes `grid-template-columns` INLINE on this
   element on every render, from `OFF_COLS` - 244px for Delta Omicron and the
   Executive board, 196px for chairs, collegiate and faculty, because a
   committee seat is not a vice presidency. An inline style beats this rule, so
   EDITING THIS VALUE ALONE DOES NOTHING. Change OFF_COLS, and note it is
   per-tab. This rule still matters as the value before the first render and if
   the script never runs. */
.roster { display:grid; grid-template-columns:repeat(auto-fill,minmax(min(196px,100%),1fr));
  gap:20px; margin-bottom:70px; }
/* Two shells. The card is glass and inverts with the zone; the frame inside it
   is a pale portrait mount whose FILL stays light in both zones - only its
   border inverts. A dark mount put the "portrait" prompt at 1.36:1 and made it
   invisible on every card, which is the one thing an empty frame exists to do. */
.person { position:relative; display:flex; flex-direction:column; height:100%;
  border-radius:30px; padding:14px 14px 4px;
  background:var(--glass-surface);
  -webkit-backdrop-filter:var(--glass-blur); backdrop-filter:var(--glass-blur);
  border:1px solid rgba(255,255,255,.6);
  box-shadow:0 20px 40px -15px rgba(0,0,0,.07), inset 0 1px 0 rgba(255,255,255,.7);
  transition:transform .3s var(--ease), box-shadow .3s var(--ease), background .3s var(--ease); }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) {
  .person { background:#fff; }
}
.person:hover { transform:translateY(-4px); background:rgba(255,255,255,.9);
  box-shadow:0 32px 60px -20px rgba(92,43,157,.22), 0 0 0 1px rgba(227,193,91,.5), inset 0 1px 0 rgba(255,255,255,.8); }
.person:active { transform:translateY(-1px) scale(.985); }
.person__frame { position:relative; overflow:hidden; aspect-ratio:4 / 5; border-radius:20px;
  border:1px solid #E7E2F0;
  background:linear-gradient(168deg,#F4F0FB 0%,#EFEAF7 60%,#F7F3FC 100%);
  display:grid; place-items:center; transition:box-shadow .3s var(--ease); }
.person__frame img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  transition:transform .72s var(--ease); }
.person:hover .person__frame { box-shadow:0 0 0 1px rgba(227,193,91,.45); }
.person:hover .person__frame img { transform:scale(1.05); }
/* Fixed height on the meta block and a two-line clamp on the name. Both are
   content-driven - "Estela Navarro-Martinez" wraps and "Akira Eernisse" does
   not - so no amount of grid tuning could have equalised the cards. A one-line
   name reserves the second line rather than closing the gap. */
.person__meta { width:100%; flex:1 0 auto; min-height:96px; display:flex; flex-direction:column;
  align-items:flex-start; gap:5px; padding:16px 8px 18px; border:0; background:transparent;
  text-align:left; cursor:pointer; font-family:inherit; }
/* Bebas caps at --gold-deep, not the official gold: #E3C15B on white measures
   about 1.8:1 as small text, and #8f6804 exists for exactly this case at 5.1. */
.person__office { font-family:var(--font-label); font-size:12px; letter-spacing:.2em;
  line-height:1.3; color:var(--gold-deep); text-transform:uppercase; }
/* The office is named ONCE. There is no abbreviation line above the name -
   "SVP" over "Senior Vice President" labelled the same seat twice, and on
   President, Chancellor and Webmaster it was the same word in caps and then in
   title case. The full form wins: it needs no decoding by a prospective member
   who does not know the Fraternity's abbreviations yet. */
/* min-height:2.4em RESERVES BOTH LINES ON THE NAME ITSELF - 2 x the 1.2
   line-height, so a one-line name occupies exactly what a two-line name does.
   Do not remove it.

   WHY IT IS HERE. Card height was equalised by the grid: .roster is
   align-items:stretch and .person is height:100%, so cards in a ROW match the
   tallest. That works, and it is why this looks fine at most widths. What it
   does not do is equalise across ROWS - and .person__meta's min-height:96px
   under-reserved the two-line case by about 4.2px (its real height is 100.2),
   so any row containing a wrapped name stood ~4px taller than a row without.
   Measured before this fix: Delta Omicron gave two distinct card heights at
   320px (413.5 / 417.7) and at 900px (415.2 / 419.3).

   IT WAS REPORTED FROM SAFARI AT DESKTOP WIDTH and did not reproduce in
   Chrome there, which sent the first diagnosis after -webkit-line-clamp and
   engine differences. That was the wrong trail: the defect is real in every
   browser, and the engines only differ in WHICH width makes a name wrap. This
   fix makes every card identical by construction, so it no longer matters.

   .person__meta's min-height:96px below is now dead - the name reserves more
   than that on its own - and is left alone rather than removed, because
   nothing was measured with it gone. */
.person__name { font-family:var(--font-display); font-size:19px; line-height:1.2;
  letter-spacing:-.02em; font-weight:400; color:var(--ink); margin:0;
  min-height:2.4em;
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; }
[data-zone="dusk"] .person { background:rgba(29,11,56,.82);
  border-color:rgba(227,193,91,.22);
  box-shadow:0 20px 44px -18px rgba(8,0,20,.5), inset 0 1px 0 rgba(255,255,255,.06); }
[data-zone="dusk"] .person:hover { background:rgba(38,16,72,.9);
  box-shadow:0 34px 64px -20px rgba(8,0,20,.6), 0 0 0 1px rgba(227,193,91,.55); }
[data-zone="dusk"] .person__frame { border-color:rgba(227,193,91,.34); }
[data-zone="dusk"] .person__name { color:var(--ink-invert); }
[data-zone="dusk"] .person__office { color:var(--gold-old); }
@media (prefers-reduced-motion:reduce) {
  .person, .person__frame img { transition:none !important; }
  .person:hover { transform:none; }
  .person:hover .person__frame img { transform:none; }
}
.rosternote { border-radius:32px; padding:52px 44px; margin-bottom:70px; text-align:center; }
.rosternote p { max-width:46ch; margin:0 auto; font-size:16px; line-height:1.65; text-wrap:pretty; }

/* ---- Faculty roll --------------------------------------------------------
   A dated roll of honour: two columns of hairline rows, name left, term
   right. No photos - the Fraternity publishes none, and a wall of empty
   frames would read as neglect.

   THE DATE IS A SEMESTER, NOT A DAY. The source tuples in officers.js keep
   the full date so the record stays intact, but the public site carries only
   limited member data and the month-and-day of a named person's initiation is
   more than this roll needs to do its job. site.js reduces it on render;
   Spring is Mar-May, Fall is Aug-Dec. */
.roll { margin:0 0 70px; }
.roll__head { display:flex; align-items:baseline; justify-content:space-between; gap:20px;
  flex-wrap:wrap; padding-bottom:16px; border-bottom:1px solid rgba(74,26,134,.16); }
.roll__title { font-family:var(--font-display); font-size:23px; line-height:1.1;
  letter-spacing:-.02em; font-weight:400; color:var(--ink); margin:0; }
.roll__count { font-family:var(--font-label); font-size:13px; letter-spacing:.2em;
  color:var(--gold-deep); margin:0; }
.roll__grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(360px,100%),1fr));
  column-gap:44px; }
.roll__row { display:flex; align-items:baseline; justify-content:space-between; gap:18px;
  padding:13px 2px; border-bottom:1px solid rgba(74,26,134,.1);
  transition:background .28s var(--ease), padding .28s var(--ease); }
.roll__row:hover { background:rgba(247,185,16,.07); padding-left:10px; padding-right:10px;
  border-radius:8px; }
.roll__name { font-size:16px; line-height:1.4; color:var(--ink); }
.roll__date { font-family:var(--font-label); font-size:14px; letter-spacing:.12em;
  color:var(--ink-muted); white-space:nowrap; font-variant-numeric:tabular-nums; }
.roll__more { margin-top:22px; font-family:var(--font-label); font-size:13px; letter-spacing:.18em;
  padding:11px 22px; border-radius:999px; cursor:pointer; min-height:var(--hit);
  background:rgba(255,255,255,.08); border:1px solid rgba(227,193,91,.34); color:var(--gold-old);
  transition:background .3s var(--ease), border-color .3s var(--ease); }
.roll__more:hover { background:rgba(255,255,255,.14); border-color:rgba(227,193,91,.6); }
[data-zone="dusk"] .roll__title { color:#fff; }
[data-zone="dusk"] .roll__name { color:rgba(245,245,247,.88); }
[data-zone="dusk"] .roll__date { color:rgba(245,245,247,.55); }
[data-zone="dusk"] .roll__count { color:var(--gold-old); }
[data-zone="dusk"] .roll__head, [data-zone="dusk"] .roll__row { border-color:rgba(255,255,255,.14); }
[data-zone="dusk"] .roll__row:hover { background:rgba(246,196,69,.1); }
@media (prefers-reduced-motion:reduce) { .roll__row, .roll__more { transition:none !important; } }

/* ---- Outward governance --------------------------------------------------
   Text only, no photos. The rule above each card widens as the office sits
   further from the chapter, so the row reads as one outward measure rather
   than four equal titles. The glow behind it is the design's single permitted
   ornament and is decoration only - aria-hidden, pointer-events:none. */
.govwrap { position:relative; padding-bottom:120px; }
.govglow { position:absolute; inset:-60px -40px 40px; pointer-events:none;
  background:radial-gradient(46% 62% at 14% 46%, rgba(247,185,16,.20) 0%, rgba(247,185,16,0) 68%),
             radial-gradient(52% 66% at 86% 54%, rgba(122,66,196,.24) 0%, rgba(122,66,196,0) 70%); }
.govlabel { position:relative; z-index:1; font-family:var(--font-body); font-size:12px;
  font-weight:700; letter-spacing:.02em; text-transform:uppercase; color:var(--ink-muted);
  margin:0 0 18px; }
.reg { position:relative; z-index:1; display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(238px,100%),1fr)); gap:14px; }
.reg__cell { display:flex; flex-direction:column; align-items:flex-start; gap:7px;
  padding:20px 22px 22px; border-radius:22px; background:#fff; border:1px solid #E3E0EA;
  box-shadow:0 2px 10px rgba(36,0,74,.06);
  transition:transform .3s var(--ease), box-shadow .3s var(--ease); }
.reg__cell:hover { transform:translateY(-3px);
  box-shadow:0 24px 44px -18px rgba(92,43,157,.26), 0 0 0 1px rgba(227,193,91,.55); }
.reg__bar { display:block; height:2px; border-radius:2px; background:var(--gold-old); margin-bottom:3px; }
.reg__role { font-family:var(--font-label); font-size:10.5px; letter-spacing:.22em;
  color:var(--gold-deep); margin:0; }
/* Lato, not Georgia. These are names in a dense row, not display type. */
.reg__name { font-family:var(--font-body); font-size:15px; font-weight:700;
  letter-spacing:-.015em; color:var(--ink-slate); margin:0; }
.reg__desc { font-size:13.5px; line-height:1.55; letter-spacing:-.005em;
  color:var(--ink-secondary); margin:0; text-wrap:pretty; }
.reg__link { color:var(--purple-mid); font-size:13px; font-weight:700; letter-spacing:-.01em; }
.reg__link:hover { color:var(--purple); }
.govnote { position:relative; z-index:1; font-size:13.5px; line-height:1.7;
  color:var(--ink-secondary); margin:20px 0 0; max-width:66ch; text-wrap:pretty; }
[data-zone="dusk"] .reg__cell { background:rgba(29,11,56,.82); border-color:rgba(227,193,91,.22); }
[data-zone="dusk"] .reg__role { color:var(--gold-old); }
[data-zone="dusk"] .reg__name { color:var(--ink-invert); }
[data-zone="dusk"] .reg__desc { color:#C9C1DA; }
[data-zone="dusk"] .reg__link, [data-zone="dusk"] .govnote a { color:var(--gold-old); }
[data-zone="dusk"] .govlabel { color:#D8D2E4; }
@media (prefers-reduced-motion:reduce) {
  .reg__cell { transition:none !important; }
  .reg__cell:hover { transform:none; }
}

/* ---- Officer detail ------------------------------------------------------
   A light sheet above the canvas, so it is contrast-locked regardless of the
   zone underneath it. */
.omodal { position:fixed; inset:0; z-index:200; background:rgba(20,0,42,.42);
  -webkit-backdrop-filter:blur(18px); backdrop-filter:blur(18px);
  display:grid; place-items:center; padding:24px; animation:fadeUp .3s ease both; }
.osheet { position:relative; width:min(520px,100%); max-height:calc(100vh - 48px); overflow-y:auto;
  background:linear-gradient(180deg,rgba(255,255,255,.96),rgba(255,255,255,.88));
  -webkit-backdrop-filter:blur(30px) saturate(150%); backdrop-filter:blur(30px) saturate(150%);
  border:1px solid rgba(255,255,255,.9); border-radius:34px; padding:52px 44px 44px;
  text-align:center;
  box-shadow:0 2px 4px rgba(20,0,42,.06), 0 40px 100px -30px rgba(20,0,42,.5); }
.osheet, .osheet h3, .osheet p, .osheet span { color:var(--ink-slate); }
.oclose { position:absolute; top:20px; right:20px; width:36px; height:36px; border-radius:50%;
  border:1px solid rgba(36,0,74,.1); background:rgba(255,255,255,.7); color:var(--ink-secondary);
  font-size:16px; line-height:1; cursor:pointer; }
.omono { width:108px; height:108px; margin:0 auto 26px; border-radius:28px;
  background:rgba(26,21,36,.05); border:1px solid rgba(26,21,36,.06);
  display:grid; place-items:center; }
.omono span { font-family:var(--font-label); font-size:42px; letter-spacing:.06em;
  color:var(--ink-secondary); }
.oeyebrow { font-family:var(--font-label); font-size:12.5px; letter-spacing:.24em;
  color:var(--gold-deep); margin:0 0 10px; }
.osheet h3 { font-size:29px; line-height:1.08; letter-spacing:-.032em; font-weight:700;
  color:var(--ink); margin:0 0 8px; }
.ometa { font-size:14px; color:#6b6478; margin:0 0 20px; }
.ometa:empty { display:none; }
.oblurb { font-size:16px; line-height:1.7; color:var(--ink-secondary); margin:0 0 28px; text-wrap:pretty; }
/* Ghost and solid, one pair. The circle borrows the pill's own purple at low
   alpha and the same lifted shadow, so the two read as one object at rest and
   hover simply fills the ghost to match the solid. */
.oactions { display:flex; align-items:center; justify-content:center; gap:14px; }
.olink { display:grid; place-items:center; width:var(--hit); height:var(--hit); flex:0 0 auto;
  border-radius:50%; border:1.5px solid rgba(85,0,125,.24); background:#fff; color:#55007d;
  box-shadow:0 6px 16px -9px rgba(85,0,125,.42);
  transition:background .3s var(--ease), border-color .3s var(--ease), color .3s var(--ease),
             box-shadow .3s var(--ease), transform .3s var(--ease); }
.olink svg { width:19px; height:19px; }
.olink:hover { background:#55007d; border-color:#55007d; color:#fff; transform:translateY(-1px);
  box-shadow:0 10px 22px -9px rgba(85,0,125,.55); }
/* One label on every card. It used to interpolate the office - "Email
   PRESIDENT", "Email HISTORIAN" - which shouted, jumped in width between
   cards, and read as nonsense on the open seats, whose address is the
   president's inbox anyway. The sheet states the office and the name directly
   above; the button only has to be the action. */
.omail { display:inline-flex; align-items:center; background:#55007d; color:#fff;
  font-size:14.5px; font-weight:700; padding:0 30px; min-height:var(--hit);
  border-radius:999px; box-shadow:0 8px 20px -8px rgba(85,0,125,.5); }
.omail:hover { background:#661492; color:#fff; }
@media (prefers-reduced-motion:reduce) {
  .omodal { animation:none; }
  .olink { transition:none !important; }
  .olink:hover { transform:none; }
}

/* ---- Resources: three doors --------------------------------------------- */
.doorgrid { display:grid; grid-template-columns:1fr; gap:16px; margin-top:52px; }
@media (min-width:900px) { .doorgrid { grid-template-columns:repeat(3,1fr); } }
.door { display:flex; flex-direction:column; border:1px solid rgba(255,255,255,.13);
  border-radius:26px; padding:32px 30px; background:rgba(255,255,255,.04); }
/* !important, because the zone's blanket `p` rule would otherwise repaint this
   the same off-white as the body copy under it and the door would lose its
   only gold. Every other eyebrow on the site is gold on the dusk half; this
   one is written as gold in the design and has to actually land that way. */
.door__eyebrow { font-family:var(--font-label); font-size:11.5px; letter-spacing:.3em;
  color:var(--gold-old) !important; margin:0 0 14px; }
.door h3 { font-family:var(--font-display); font-size:26px; line-height:1.1;
  letter-spacing:-.02em; font-weight:400; color:#fff; margin:0 0 10px; }
.door > p { font-size:15px; line-height:1.62; color:rgba(255,255,255,.68);
  margin:0 0 26px; text-wrap:pretty; }
.door__links { display:flex; flex-direction:column; gap:13px; align-items:flex-start; margin-top:auto; }
.door__links a { display:inline-flex; align-items:center; gap:8px; font-size:14.5px;
  font-weight:700; letter-spacing:-.01em; color:var(--gold-pale);
  transition:opacity .28s var(--ease); }
.door__links a:hover { opacity:.62; color:var(--gold-pale); }
.door__glyph { font-family:system-ui,sans-serif; font-size:12px; letter-spacing:0; }
/* Two separate gated areas on the Fraternity's side, each prompting its own
   sign-in. We cannot fix their auth; we can stop it being a surprise. */
.door__gated { font-family:var(--font-body); font-weight:400; font-size:12px;
  letter-spacing:0; color:rgba(255,255,255,.72); }

/* A band, not a fourth card: the section already has three, and a fourth
   orphans itself at the two-across breakpoint. */
.fundband { margin-top:66px; border-top:1px solid rgba(255,255,255,.14); padding-top:42px;
  display:grid; grid-template-columns:repeat(auto-fit,minmax(min(330px,100%),1fr));
  gap:22px 60px; align-items:center; }
.fundband h3 { font-family:var(--font-display); font-size:clamp(21px,2.3vw,28px);
  line-height:1.16; letter-spacing:-.02em; font-weight:400; color:#fff; margin:0 0 10px; }
.fundband p { font-size:15.5px; line-height:1.66; color:rgba(255,255,255,.7);
  margin:0; max-width:56ch; text-wrap:pretty; }
/* Tinted gold, not the gold pill. The prominent gold fill is reserved for
   Apply and must not have a twin. */
.btn--goldtint { background-color:rgba(227,193,91,.16); color:#F1E4C4; }
.btn--goldtint:hover { background-color:rgba(227,193,91,.26); color:#F1E4C4; }

/* ---- Gateway -------------------------------------------------------------
   THE CARD INVERTS WITH THE ZONE. It is the same glass the roster cards and
   the member doors use, so the three blocks on the purple half read as one
   family rather than three treatments.

   Without the inversion below this was a light glass card carrying white
   headings - the zone's blanket h1-h4 rule reaches straight into it, because
   glass is not an opaque own-surface the way .card and .purposecard are. That
   is white on 78%-white. The gold pill and the quiet second path already have
   their own dusk rules, so only the card, the headline and the body need it. */
.gateway { border-radius:32px; padding:78px 64px; text-align:center; }
[data-zone="dusk"] .gateway { background:rgba(29,11,56,.82);
  border-color:rgba(227,193,91,.22);
  box-shadow:0 24px 50px -20px rgba(8,0,20,.5), inset 0 1px 0 rgba(255,255,255,.06); }
[data-zone="dusk"] .gateway h3 { color:var(--ink-invert); }
[data-zone="dusk"] .gateway p, [data-zone="dusk"] .gateway .lede { color:rgba(245,245,247,.72); }
.gateway h3 { font-size:clamp(34px,5.4vw,64px); line-height:1; letter-spacing:-.04em;
  font-weight:700; color:var(--ink); margin:0 0 20px; }
.gateway__row { display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:14px 30px; margin-top:42px; }
@media (max-width:640px) { .gateway { padding:52px 26px; } }

/* ---- Footer --------------------------------------------------------------
   FOUR columns, not three. The first is the lockup - wordmark, postal
   address, socials - and it is the column that says who this site belongs to.
   It was missing entirely, which took the chapter's LinkedIn and its postal
   address off the site with it. */
/* SAFE AREA. The DC's whole "Safe areas" section is this one rule, and it was
   never ported - the footer ended at a flat 40px, so on any iPhone with a home
   indicator its bottom ~34px sat underneath it.
   ⚠️ UNVERIFIED ON HARDWARE. Headless Chrome resolves every
   env(safe-area-inset-*) to 0px - it has no notch or home-indicator emulation -
   so the max() always falls through to 40px here and this environment will
   report it fixed whether it is or not. A real handset reading is still owed. */
.foot { padding:90px 0 max(40px, env(safe-area-inset-bottom));
  color:rgba(255,255,255,.55); }
.goldmark { font-family:var(--font-display); font-weight:400; letter-spacing:.12em;
  background:linear-gradient(100deg,#8A5F12 0%,#C9992F 12%,#FBEBB4 28%,#E8BE55 38%,#C08B22 50%,#F3DE9A 60%,#FDF1C6 68%,#DFB44A 78%,#A97516 90%,#D9A93A 100%);
  background-size:200% 100%; -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent; animation:goldDrift 9s linear infinite; }
.goldline { font-family:var(--font-display); font-weight:400; font-size:16.5px; letter-spacing:.01em;
  background:linear-gradient(100deg,#C9992F 0%,#FBEBB4 28%,#DFB44A 55%,#FDF1C6 74%,#C9992F 100%);
  background-size:200% 100%; -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent; animation:goldDrift 9s linear infinite; }
.foot__grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));
  gap:40px; margin-bottom:60px; }
.foot__mark { font-size:52px; line-height:1; margin-bottom:22px; }
.foot__addr { font-size:15px; line-height:1.75; color:rgba(255,255,255,.68);
  margin:0 0 24px; letter-spacing:.01em; }
.foot__socials { display:flex; gap:10px; }
.social { width:var(--hit); height:var(--hit); display:grid; place-items:center; border-radius:50%;
  background:rgba(255,255,255,.14); border:1px solid rgba(255,255,255,.26);
  color:rgba(255,255,255,.86);
  transition:transform .5s var(--ease), background .5s var(--ease), color .5s var(--ease); }
.social svg { width:19px; height:19px; }
/* Every stroked glyph on the dusk half is gold - the footer socials most
   visibly. The badges opt back out: they colour their own glyph through
   currentColor and this would override it. */
[data-zone="dusk"] svg[stroke] { stroke:var(--gold-old); }
[data-zone="dusk"] .badge svg { stroke:currentColor; }
[data-zone="dusk"] .social:hover svg { stroke:var(--gold); }
.social:hover { transform:translateY(-3px); background:rgba(246,196,69,.22);
  color:var(--gold); border-color:rgba(246,196,69,.4); }
/* Column headings are pale champagne and must win inside the dusk zone - the
   footer IS the zoned element, so the blanket heading rule sits at the same
   specificity and would otherwise take them to plain white. */
.foot h2 { font-family:var(--font-label); font-size:14px; letter-spacing:.24em;
  color:var(--gold-pale) !important; margin:0 0 18px; font-weight:400; }
.foot ul { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:10px; }
.foot a:not(.social) { color:rgba(255,255,255,.6); font-size:14.5px; }
.foot a:hover { color:#fff; }
.foot__seal { display:flex; justify-content:center; padding:0 0 40px; }
.foot__seal img { width:200px; height:auto; }
/* .55 on 12px copy is 4.4:1 - just under. Legal type is the smallest on the
   page and the least forgiving; it gets .74. */
.foot__bottom { display:flex; flex-wrap:wrap; gap:14px; justify-content:space-between;
  padding-top:26px; border-top:1px solid rgba(255,255,255,.12);
  font-size:12.5px; color:rgba(255,255,255,.74); }
.foot__fine { font-size:12px; line-height:1.7; color:rgba(255,255,255,.3);
  margin:22px 0 0; max-width:800px; }
.foot__fine a { font-size:12px; color:rgba(255,255,255,.5); }
@media (prefers-reduced-motion:reduce) { .social { transition:none !important; } }


/* ===========================================================================
   MOTION CONTRACT
   The rules below are driven by site.js. The attribute names and the custom
   properties are the interface between the two - renaming one without the
   other silently disables the motion.
   =========================================================================== */

/* The sheet: one continuous gradient behind everything. Sections must stay
   transparent or they punch holes in it.

   IT SPANS THE DOCUMENT, NOT THE VIEWPORT, and that is the whole point. It was
   `position:fixed; inset:0`, which is viewport-sized and does not scroll - so
   the entire dawn-to-dusk ramp was squeezed into every single screenful and
   repeated all the way down. An element's zone is computed from its position
   in the DOCUMENT, so with a viewport-mapped sheet the two had nothing to do
   with each other: anything sitting low on screen was over royal purple no
   matter which half of the page it belonged to, which is how "Four years here.
   Forty after." ended up as dark type on dark purple.

   layoutSheet() writes the height. Do not put a fixed position back on this. */
#sheet { position:absolute; top:0; left:0; right:0; height:100%;
  z-index:-2; pointer-events:none; }
#progress { height:2px; width:0; pointer-events:none;
  background:linear-gradient(90deg,var(--purple),var(--gold-signal)); }
main, .sect, section, footer { background:transparent; }

/* Item reveal. --rdur and --rdist are written by revealItems(); --rd is the
   per-element stagger delay. data-in="0" is queued, "1" is playing, and the
   attribute is REMOVED on arrival so hover and press transforms work again. */
@keyframes riseIn { from { opacity:0; transform:translateY(var(--rdist,24px)); } to { opacity:1; transform:none; } }
/* !important ON THE PRIMED STATE ONLY, and it is load-bearing. An element
   carrying its own `fadeUp` with fill-mode:both has its opacity pinned to 1 by
   that animation, and an animation beats a plain declaration whatever the
   source order - so the element would sit visible and then blink out the moment
   it is primed. Today nothing inside main/footer carries a CSS fadeUp AND gets
   queued: every fadeUp user is either inside #heroStatic (excluded by the
   reveal filter) or is #officerModal / #portal (body-level, outside the reveal
   roots). That is a coincidence of the current markup, not a guarantee, and the
   failure mode is a visible blink. The DC carries the same !important for the
   same reason.
   Deliberately NOT on [data-in="1"] - that would cancel the schedule filter's
   own remount animation. */
[data-in="0"] { opacity:0 !important; }
[data-in="1"] { animation:riseIn var(--rdur,800ms) var(--ease) var(--rd,0ms) both; }

/* Zones. layoutSheet() sets data-zone="dusk" only where the sheet is actually
   dark, and data-ink deep/bright on the ramp either side of the flip, where
   the default inks are too weak. */
[data-zone="dusk"] { color:var(--ink-invert); }
[data-zone="dusk"] h1, [data-zone="dusk"] h2, [data-zone="dusk"] h3, [data-zone="dusk"] h4 { color:#fff; }
[data-zone="dusk"] p, [data-zone="dusk"] .lede { color:rgba(245,245,247,.72); }
[data-zone="dusk"] .eyebrow, [data-zone="dusk"] .evrow__m { color:#DFCA8B; }
/* THE RAMP INKS ARE MEASURED, NOT CHOSEN BY EYE. The band runs #DFCA8B to
   #8A6A86 to #3B1B6E, and #8A6A86 is where dark and light type cross over in
   contrast - which is exactly why the flip sits there. Either side of it the
   default inks are far too weak, so these are the strongest values in family:

     at 0.648 of the document, sheet rgb(176,149,136)
       #4A3A55 (the old body ink)  3.70   FAIL
       #241033                     6.27   pass
     at 0.665, sheet rgb(140,108,134) - one notch above the flip
       #241033                     3.85
       #fff                        4.56   pass
       #E3C15B gold                2.62   FAIL - no gold on the ramp

   Body copy uses the heading ink on the deep side; nothing else reaches AA.
   On the bright side the off-white goes to .95, because .8 measured 4.19 at
   the flip. */
   EVERY VALUE BELOW IS THE DESIGN'S OWN. I derived this band independently
   from contrast measurements first and landed within a hair of it, but the
   design states it outright and these are its numbers, not mine. Do not
   substitute "equivalent" inks - the pair is chosen so they cross exactly at
   the #8A6A86 stop, which is where layoutSheet() flips. */
[data-ink="deep"] h1, [data-ink="deep"] h2, [data-ink="deep"] h3, [data-ink="deep"] h4 { color:#140F1C; }
[data-ink="deep"] p, [data-ink="deep"] li, [data-ink="deep"] .lede,
[data-ink="deep"] .faq summary, [data-ink="deep"] .roll__name { color:#140F1C; }
[data-ink="deep"] .eyebrow, [data-ink="deep"] .evrow__m { color:#3A2405; }
[data-ink="deep"] .faq summary svg { color:#4E3702; }
[data-zone="dusk"][data-ink="bright"] h1, [data-zone="dusk"][data-ink="bright"] h2,
[data-zone="dusk"][data-ink="bright"] h3, [data-zone="dusk"][data-ink="bright"] h4 { color:#fff; }
[data-zone="dusk"][data-ink="bright"] p, [data-zone="dusk"][data-ink="bright"] li,
[data-zone="dusk"][data-ink="bright"] .lede,
[data-zone="dusk"][data-ink="bright"] .faq p { color:#FFFFFF; }
[data-zone="dusk"][data-ink="bright"] .eyebrow,
[data-zone="dusk"][data-ink="bright"] .evrow__m,
[data-zone="dusk"][data-ink="bright"] .faq summary svg { color:#FFEDBC; }

/* The hairlines are part of the band, not a separate problem. A 16%-purple
   rule is invisible the moment the canvas starts to darken - which is what
   made the schedule look like it had lost its dividers. */
[data-ink="deep"] .evrow, [data-ink="deep"] .benefit, [data-ink="deep"] .faq,
[data-ink="deep"] .faq details, [data-ink="deep"] .roll__row,
[data-ink="deep"] .roll__head { border-color:rgba(20,15,28,.28); }
[data-zone="dusk"][data-ink="bright"] .evrow, [data-zone="dusk"][data-ink="bright"] .benefit,
[data-zone="dusk"][data-ink="bright"] .faq, [data-zone="dusk"][data-ink="bright"] .faq details,
[data-zone="dusk"][data-ink="bright"] .roll__row,
[data-zone="dusk"][data-ink="bright"] .roll__head { border-color:rgba(255,255,255,.34); }

/* ---- Components the blanket zone rules cannot reach ----------------------
   The rules above are element selectors at (0,1,1). Any component that names
   its own ink with a CLASS - `.faq summary`, `.evrow h4` - sits at (0,2,0) and
   outranks them, so it keeps its light-half colour straight into the purple.
   That is not theoretical: the nine FAQ questions measured 1.29:1 and the
   schedule rows bottomed out at 1.01:1, which is black on black.
   ANY new component that sets its own colour needs a line here. */
[data-zone="dusk"] .faq summary { color:var(--ink-invert); }
[data-zone="dusk"] .faq summary svg { color:var(--gold-old); }
[data-zone="dusk"] .faq p { color:rgba(245,245,247,.78); }
[data-zone="dusk"] .faq details { border-bottom-color:rgba(255,255,255,.16); }
[data-zone="dusk"] .evrow h4 { color:#fff; }
[data-zone="dusk"] .evrow p { color:rgba(245,245,247,.72); }
[data-zone="dusk"] .impact__l { color:rgba(245,245,247,.72); }
/* The date column follows the band, not the zone: gold is a mid-tone and dies
   on a mid-tone canvas, so it only returns once the sheet is properly dark. */
[data-zone="dusk"][data-ink="bright"] .evrow h4,
[data-zone="dusk"][data-ink="bright"] .evrow p,
[data-zone="dusk"][data-ink="bright"] .evrow__d,
[data-zone="dusk"][data-ink="bright"] .evrow__meta { color:#FFFFFF; }
[data-zone="dusk"] .evrow__d { color:var(--gold-old); }
/* NOTE what is absent: .evrow__d. It is a [data-stat], and the design's
   deep-ink block covers p, li, faq questions and roll names - not stats. The
   day numerals keep their purple through the ramp and only turn gold once the
   canvas is properly dark. Adding them here painted them near-black. */
[data-ink="deep"] .evrow h4, [data-ink="deep"] .evrow p,
[data-ink="deep"] .evrow__meta { color:#140F1C; }
/* An opaque card is its OWN surface - light wherever it lands - so no zone or
   ink rule may reach inside it. Three of them: the white cards, the purpose
   sheet in #chapter, and the four-step purple ramp in #values. */
.card, .card h1, .card h2, .card h3, .card h4,
.purposecard, .purposecard h1, .purposecard h2, .purposecard h3, .purposecard h4,
.vcard h3 { color:var(--ink) !important; }
/* HEADINGS ONLY — never the containers. `background:none` belongs on the
   gradient-clipped TEXT, to drop the clip and let a flat ink show; putting it
   on the list above put it on `.card` and `.purposecard` themselves and blew
   away the white fill and the purpose sheet's gradient, so both cards went
   transparent and the canvas showed straight through them.
   -webkit-text-fill-color, not just color: a clipped heading has a transparent
   fill, and `color` alone leaves it invisible. */
.card h1, .card h2, .card h3, .card h4,
.purposecard h1, .purposecard h2, .purposecard h3, .purposecard h4,
.vcard h3 { background:none !important; -webkit-text-fill-color:var(--ink) !important; }
.card p, .purposecard p, .vcard p { color:var(--ink-secondary) !important; }
.purposecard .eyebrow, .card .eyebrow,
.card .heritage__l, .card .evrow__m { color:var(--gold-deep) !important; }
/* Stat numerals on an own-surface card are GOLD, not purple. The heritage
   years and the tabling card's date both sit on white, where the design uses
   #8f6804 - the same gold the eyebrows in those cards use. */
.card .heritage__v, .card .evrow__d { color:var(--gold-deep) !important; }
/* The quoted purpose is display-scale statement, not supporting copy, so it
   takes full ink. Declared after the rule above because both are !important
   and that one is the more specific selector. */
.purposecard .purposecard__q { color:var(--ink) !important; }


/* ---- The pinned hero ----------------------------------------------------- */
.heropane { position:sticky; top:0; height:100vh; height:100svh;
  overflow:hidden; display:grid; place-items:center; }
.hero__mesh { position:absolute; inset:-8%; will-change:transform;
  background:
    radial-gradient(56% 50% at 22% 26%, rgba(178,150,236,.46) 0%, rgba(178,150,236,0) 62%),
    radial-gradient(50% 46% at 80% 20%, rgba(247,201,104,.40) 0%, rgba(247,201,104,0) 60%),
    radial-gradient(54% 50% at 74% 84%, rgba(138,168,238,.38) 0%, rgba(138,168,238,0) 62%),
    radial-gradient(48% 44% at 24% 86%, rgba(240,158,186,.32) 0%, rgba(240,158,186,0) 62%); }
.hero__wash { position:absolute; inset:0; opacity:0; will-change:opacity;
  background:linear-gradient(180deg,rgba(36,0,74,.08) 0%,rgba(36,0,74,.22) 100%); }
.hero__mark { position:absolute; top:15%; left:0; right:0; display:grid;
  grid-template-columns:1fr auto 1fr; align-items:baseline; column-gap:.5em;
  opacity:.95; will-change:transform,opacity; pointer-events:none;
  font-family:var(--font-display); font-weight:400; font-size:clamp(17px,2.1vw,32px);
  line-height:1; letter-spacing:.06em; white-space:nowrap; }
.hero__mark img { position:absolute; left:50%; top:-3.4em; transform:translateX(-50%);
  height:14em; width:auto; opacity:.2; grid-column:1/-1; }
.hero__mark .goldmark { justify-self:end; animation:none; }
.hero__mark .plummark { justify-self:start; }
/* DESCENDER GUARD, and the two declarations are a pair - never split them.
   background-clip:text paints only inside the element's box, so at
   line-height:1 any glyph overshooting it comes out transparent: the 'g' in
   "Sigma" was being sliced flat. The padding grows the paint box and the
   negative margin takes exactly the same amount back out of the layout, so
   the glyph is painted whole and nothing moves.

   Only this lockup needs it. Checked the rest: the nav ΔΣΠ|ΔΟ and the footer
   ΔΣΠ are also block-level at line-height:1 but Greek capitals have no
   descenders, and the footer's "Delta Omicron Chapter" is inline at 1.75.
   Our headings are flat-coloured, not gradient-clipped, so they cannot clip
   at all - which is why the design patches eighteen elements here and we
   patch one. */
.hero__mark .goldmark, .hero__mark .plummark,
.hero__wm .goldmark, .hero__wm .plummark { padding-block:.14em; margin-block:-.14em; }
.markrule { color:rgba(74,26,134,.42); align-self:center; transform:translateY(-.04em); font-size:.86em; }
/* On the pale lavender sheet the drifting metals read as olive and charcoal.
   These ramps are far more chromatic while staying dark enough to hold 4.5:1 -
   this mark is under the 24px large-text line, so 3:1 is not enough.

   DARKENED 9 Aug. Removing the hero's opaque white base let the sheet's
   lavender through and the ramp's lightest stop, #85590A, fell to 4.09:1
   against #DACDF6 - below AA.
   TWO stops moved, not one, and that is the point: replacing only #85590A
   promotes #7A4F05 to worst case at 4.76:1, which is a squeak rather than a
   margin. Every interior stop is now #6B4402, so the true worst case is
   5.73:1 whichever part of the ramp a glyph lands on. THE WORST CASE IS
   WHICHEVER STOP IS LIGHTEST - darkening one only hands the problem to the
   next. The ends stay #4E3000 so the mark still reads as a ramp. */
.hero__mark .goldmark, .hero__wm .goldmark { background:linear-gradient(100deg,#4E3000 0%,#6B4402 18%,#6B4402 34%,#6B4402 50%,#6B4402 66%,#6B4402 84%,#4E3000 100%);
  background-size:100% 100%; -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent; filter:none;
  /* base .goldmark drifts; both hero marks are static */
  animation:none; }
.plummark { background:linear-gradient(100deg,#2A0058 0%,#3D0080 20%,#4E0F9C 38%,#5A1AAE 50%,#45108C 66%,#33006B 84%,#26004F 100%);
  background-size:100% 100%; -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent; }
.hero__beats { position:relative; z-index:2; width:100%; max-width:1040px;
  padding:8vh 28px 0; text-align:center; display:grid; place-items:center; }
.hero__beats > [data-beat] { grid-area:1/1; will-change:transform,opacity; }
.hero__beats h1 { font-family:var(--font-display); font-size:clamp(28px,4.4vw,66px);
  line-height:1.08; letter-spacing:-.018em; font-weight:400; color:var(--ink);
  margin:0 auto; max-width:21ch; text-wrap:balance; }
.hero__beats h2 { font-family:var(--font-display); font-size:clamp(25px,4vw,58px);
  line-height:1.1; letter-spacing:-.018em; font-weight:400; color:var(--ink);
  margin:0 0 24px; max-width:24ch; text-wrap:balance; }
.hero__sub { font-size:clamp(14.5px,1.5vw,18px); line-height:1.6;
  color:var(--ink-secondary); margin:0 auto; max-width:46ch; }
.hero__beats .hero__kicker { font-family:var(--font-label);
  font-size:clamp(11px,1.15vw,14px); letter-spacing:.42em; color:var(--gold-darkest);
  margin:0 0 30px; text-shadow:0 1px 2px rgba(255,255,255,.85),0 0 7px rgba(255,255,255,.7); }
/* hairline rules flanking the establishment line */
[data-rule] { display:flex; align-items:center; justify-content:center; gap:20px; }
[data-rule]::before, [data-rule]::after { content:""; height:1px; width:clamp(28px,7vw,86px);
  background:linear-gradient(90deg,rgba(107,74,2,0),rgba(107,74,2,.42)); }
[data-rule]::after { background:linear-gradient(90deg,rgba(107,74,2,.42),rgba(107,74,2,0)); }

@media (prefers-reduced-motion:reduce) {
  #heroPin { height:auto !important; }
  .heropane { position:static; height:auto; padding:120px 0; }
  .hero__beats > [data-beat] { grid-area:auto; opacity:1 !important; transform:none !important; }
  [data-in] { animation:none !important; opacity:1 !important; }
}

/* ===========================================================================
   THE PORTAL - the four forms, in one sheet over the page.

   Ported from the design's application portal (data-pscrim / data-pcard /
   data-field / data-lgbtn and friends). Four branches share one shell: the
   application (three steps), "keep me posted", the alumni registry, and a
   collegiate brother's record.

   IT IS ITS OWN SURFACE, AND THAT IS WHY IT LIVES OUTSIDE main.
   #portal is a direct child of body, so no [data-zone] or [data-ink] selector
   can reach inside it - the sheet is always a light glass card on a dark
   scrim, wherever the page behind it happens to be on the dawn-to-dusk ramp.
   Move it inside a section and the dusk rules will repaint every paragraph in
   it off-white on near-white. If you need to check that, the surface rule is
   written up in RECONCILIATION.md.

   GEORGIA WEIGHTS. The design declares font-weight:500 on the sheet title and
   on the branch-button headings. Georgia has 400 and 700 and nothing between,
   and CSS font matching resolves a requested 500 DOWN to 400 - so 500 renders
   as regular and 400 is what must be written here. Do not "correct" these to
   500 or 700; see the Georgia note at the top of this file.
   =========================================================================== */
.pscrim { position:fixed; inset:0; z-index:300; overflow-y:auto;
  background-color:rgba(30,12,56,.88);
  backdrop-filter:blur(32px) saturate(180%);
  -webkit-backdrop-filter:blur(32px) saturate(180%);
  animation:fadeUp .45s var(--ease) forwards; }
.pwrap { max-width:660px; margin:0 auto; padding:64px 24px 110px; text-align:center; }
.pcrest { width:108px; margin:0 auto 26px; display:block;
  filter:drop-shadow(0 18px 40px rgba(227,193,91,.28)); }
.peyebrow { font-family:var(--font-label); font-size:12.5px; letter-spacing:.34em;
  color:var(--gold-old); margin:0 0 14px; text-transform:uppercase; }
.ptitle { font-family:var(--font-display); font-size:clamp(27px,4.3vw,48px);
  line-height:1.06; letter-spacing:-.022em; font-weight:400;
  color:var(--ink-invert); margin:0 0 12px; }
.pnote { font-size:16px; line-height:1.6; color:rgba(245,245,247,.66);
  margin:0 auto 38px; max-width:44ch; }

/* The close button is the glass control, dark variant - it floats over the
   scrim rather than sitting on the card. */
.pclose { position:fixed; top:22px; right:24px; z-index:2;
  width:44px; height:44px; border-radius:50%; line-height:1; font-size:17px;
  cursor:pointer; color:var(--gold-cream);
  border:1px solid rgba(255,255,255,.24);
  background-color:rgba(255,255,255,.14);
  backdrop-filter:blur(20px); -webkit-backdrop-filter:blur(20px);
  transition:background-color 200ms ease, transform 120ms var(--ease); }
.pclose:hover { background-color:rgba(255,255,255,.24); }
.pclose:active { transform:scale(.96); opacity:.8; transition-duration:80ms; }

.pcard { border-radius:36px; background:var(--glass-surface);
  backdrop-filter:blur(24px) saturate(180%);
  -webkit-backdrop-filter:blur(24px) saturate(180%);
  border:1px solid rgba(255,255,255,.7);
  box-shadow:0 30px 60px -22px rgba(0,0,0,.4),
             0 0 1px 1px rgba(255,255,255,.85) inset; }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) {
  .pcard { background:#fff; }
  .pscrim { background-color:rgba(30,12,56,.97); }
}

/* ---- the branch screen ---- */
.pbranch { padding:46px 40px; }
.pbranch__q { font-family:var(--font-display); font-size:20px; line-height:1.35;
  color:var(--ink); margin:0 0 28px; font-weight:400; }
.pbranch__row { display:grid; gap:14px;
  grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr)); }
.branchbtn { display:flex; flex-direction:column; gap:7px; text-align:left;
  cursor:pointer; padding:22px 22px 24px; border-radius:22px;
  background:rgba(255,255,255,.6); border:1px solid rgba(36,0,74,.12);
  transition:transform .4s var(--ease), background .4s ease,
             border-color .4s ease, box-shadow .4s ease; }
.branchbtn:hover:not(:disabled) { transform:translateY(-3px); background:#fff;
  border-color:rgba(107,74,2,.4); box-shadow:0 16px 34px -16px rgba(36,0,74,.3); }
.branchbtn:active:not(:disabled) { transform:scale(.96); opacity:.8; transition-duration:80ms; }
.branchbtn:disabled { opacity:.5; cursor:not-allowed; }
.branchbtn[data-primary="true"] { background:rgba(255,255,255,.88);
  border-color:rgba(227,193,91,.7);
  box-shadow:0 12px 28px -14px rgba(36,0,74,.3), 0 0 0 1.5px var(--gold-old) inset,
             0 0 18px -4px rgba(227,193,91,.45); }
.branchbtn[data-primary="true"] .bhead { color:var(--purple-deep); }
.bhead { font-family:var(--font-display); font-size:19px; font-weight:400;
  letter-spacing:-.018em; color:var(--ink); }
.bsub { font-size:13.5px; line-height:1.5; color:var(--ink-secondary); }

/* ---- the step meter, application only ---- */
.psteps { display:flex; align-items:center; gap:10px; margin-bottom:30px; }
.psteps > div { flex:1 1 0; display:flex; flex-direction:column; gap:8px; }
.psteps b { height:3px; border-radius:999px; background:rgba(36,0,74,.13);
  transition:background .5s var(--ease); }
.psteps > div[data-on="true"] b { background:linear-gradient(90deg,var(--gold-darkest),var(--purple-royal)); }
.psteps span { font-family:var(--font-label); font-size:10.5px; letter-spacing:.2em;
  color:var(--ink-muted); text-transform:uppercase; }
.psteps > div[data-on="true"] span { color:var(--purple-royal); }

/* ---- the form itself ---- */
.pform { text-align:left; padding:44px 40px 40px; }
/* One column on a phone; above 560px short related fields pair up, so a form
   does not LOOK long before anyone has read a word of it. */
.fieldgrid { display:grid; gap:20px; grid-template-columns:1fr; }
@media (min-width:560px) {
  .fieldgrid { grid-template-columns:1fr 1fr; }
  .cell { grid-column:1 / -1; }
  .cell[data-half="true"] { grid-column:span 1; }
}
.field { position:relative; display:block; }
.finput { width:100%; font-size:16px; padding:26px 18px 12px; border-radius:16px;
  border:1px solid rgba(36,0,74,.14); background:rgba(255,255,255,.72);
  color:var(--ink); outline:none; appearance:none; font-family:var(--font-body);
  transition:border-color .35s var(--ease), box-shadow .35s var(--ease), background .35s; }
textarea.finput { resize:vertical; min-height:120px; }
.floating { position:absolute; left:19px; top:20px; pointer-events:none;
  font-family:var(--font-label); font-size:14px; letter-spacing:.18em;
  color:var(--ink-muted); text-transform:uppercase;
  transition:transform .32s var(--ease), color .32s ease, font-size .32s ease;
  transform-origin:0 0; }
/* The raised state. :not(:placeholder-shown) is why every text box carries
   placeholder=" " - without a placeholder the selector never matches and the
   label sits on top of what is being typed. A select has no placeholder state,
   so it is raised unconditionally. */
.field .finput:focus ~ .floating,
.field .finput:not(:placeholder-shown) ~ .floating,
.field select.finput ~ .floating { transform:translateY(-11px) scale(.82); color:var(--gold-deep); }
.field .finput:focus { border-color:var(--gold-old); background:rgba(255,255,255,.9);
  box-shadow:0 0 0 4px rgba(227,193,91,.28), inset 0 1px 3px rgba(36,0,74,.05); }
/* Only after a submit attempt - :invalid alone marks every required field red
   the moment the form opens, before anyone has typed anything. */
.pform[data-tried="true"] .finput:invalid { border-color:#C40043; }

.flabel { font-family:var(--font-label); font-size:11.5px; letter-spacing:.22em;
  color:var(--gold-darkest); margin:0 0 12px; text-transform:uppercase; }
.consent { display:flex; align-items:flex-start; gap:11px; cursor:pointer;
  font-size:14.5px; line-height:1.5; color:var(--ink); }
.consent input { width:19px; height:19px; flex:0 0 auto; margin-top:1px;
  accent-color:var(--purple-royal); }
.tag { cursor:pointer; }
.tag input { position:absolute; opacity:0; pointer-events:none; }
.tag span { display:inline-block; padding:8px 15px; border-radius:999px;
  font-size:13px; color:var(--purple-royal); background:rgba(255,255,255,.6);
  border:1px solid rgba(36,0,74,.14);
  transition:background .35s ease, border-color .35s ease, color .35s ease; }
.tag input:checked + span { background:linear-gradient(180deg,var(--purple-royal),var(--purple-deep));
  border-color:var(--purple-deep); color:var(--gold-cream); }
.tag input:focus-visible + span { outline:2px solid var(--gold-deep); outline-offset:2px; }
.tagrow { display:flex; flex-wrap:wrap; gap:8px; }

/* Liquid glass submit. Translucent purple over the card behind it, with a
   specular top edge and a hairline light ring, so it reads as a lens rather
   than a flat fill. Deliberately NOT .btn--prominent: that rule sets a flat
   background-color, which would flatten this gradient. */
.lgbtn { position:relative; isolation:isolate; overflow:hidden;
  display:inline-flex; align-items:center; justify-content:center; gap:10px;
  font-family:var(--font-body); font-size:16px; font-weight:700;
  letter-spacing:-.012em; padding:18px 34px; border-radius:999px; border:0;
  cursor:pointer; color:#FFF6DC;
  background:linear-gradient(180deg, rgba(88,42,156,.94) 0%,
             rgba(48,14,92,.97) 58%, rgba(34,8,68,.98) 100%);
  backdrop-filter:blur(22px) saturate(200%); -webkit-backdrop-filter:blur(22px) saturate(200%);
  box-shadow:0 1px 0 rgba(255,255,255,.34) inset,
             0 0 0 1px rgba(255,255,255,.13) inset,
             0 -10px 22px -12px rgba(0,0,0,.5) inset,
             0 16px 36px -14px rgba(29,11,56,.6);
  transition:transform .28s var(--ease), box-shadow .28s var(--ease); }
/* the specular - a soft light pooled along the top third, as on real glass */
.lgbtn::before { content:''; position:absolute; z-index:-1; left:6%; right:6%;
  top:0; height:56%; border-radius:inherit; pointer-events:none;
  background:linear-gradient(180deg, rgba(255,255,255,.26), rgba(255,255,255,0)); }
.lgbtn:hover { transform:translateY(-1px);
  box-shadow:0 1px 0 rgba(255,255,255,.42) inset,
             0 0 0 1px rgba(255,255,255,.2) inset,
             0 -10px 22px -12px rgba(0,0,0,.45) inset,
             0 24px 48px -16px rgba(29,11,56,.66); }
.lgbtn:active { transform:scale(.985); transition-duration:80ms; }
.lgbtn[disabled] { opacity:.55; cursor:progress; }
.pback { font-family:var(--font-body); font-size:15px; font-weight:700;
  letter-spacing:-.01em; background:transparent; border:0; box-shadow:none;
  color:var(--purple); border-radius:999px; padding:0 16px; min-height:44px;
  display:inline-flex; align-items:center; cursor:pointer;
  transition:background-color 200ms ease, transform 120ms var(--ease); }
.pback:hover { background-color:rgba(51,0,102,.08); }
.pback:active { transform:scale(.96); opacity:.8; transition-duration:80ms; }
.pactions { display:flex; flex-wrap:wrap; align-items:center; gap:12px; margin-top:8px; }
.pactions .lgbtn { flex:1 1 auto; }
.pfine { font-size:12px; line-height:1.75; letter-spacing:-.01em;
  color:#5B5468; margin:6px 0 0; text-align:left; }
.perror { font-size:14px; line-height:1.6; color:#C40043; margin:6px 0 0;
  text-align:left; font-weight:700; }

/* ---- the sent screen ---- */
.psent { padding:70px 44px; animation:fadeUp .5s var(--ease) both; }
.pdone { font-family:var(--font-label); font-size:56px; letter-spacing:.04em;
  color:var(--purple); line-height:1; margin-bottom:14px; text-transform:uppercase; }
.psent p { font-size:16px; line-height:1.65; color:var(--ink-secondary);
  margin:0 auto 20px; max-width:40ch; }
.psent p + p { font-size:15px; margin-bottom:30px; }
.psent a { font-weight:700; }
.pbackbtn { background:linear-gradient(180deg,var(--purple-royal),var(--purple-deep));
  color:var(--gold-cream); font-family:var(--font-label); font-size:13px;
  letter-spacing:.2em; text-transform:uppercase; padding:14px 34px; border:0; border-radius:999px;
  cursor:pointer; min-height:44px;
  transition:transform 120ms var(--ease), opacity 100ms ease; }
.pbackbtn:active { transform:scale(.96); opacity:.8; transition-duration:80ms; }

@media (max-width:560px) {
  .pwrap { padding:56px 16px 90px; }
  .pform { padding:32px 22px 30px; }
  .pbranch, .psent { padding:34px 22px; }
}
@media (pointer:coarse) {
  .branchbtn, .lgbtn, .pback, .pbackbtn, .pclose { min-height:var(--hit); }
  .tag span { display:inline-flex; align-items:center; min-height:var(--hit); }
  .consent { min-height:var(--hit); }
  .consent input { width:26px; height:26px; }
}
@media (prefers-reduced-motion:reduce) {
  .pscrim, .psent { animation:none; }
  .branchbtn, .lgbtn, .finput, .floating, .tag span, .psteps b,
  .pclose, .pback, .pbackbtn { transition:none !important; }
  .branchbtn:hover:not(:disabled), .lgbtn:hover { transform:none; }
}
/* ===========================================================================
   BACK TO TOP — ported from the design export, [data-totop] at DC ~1476-1499.

   It sat in the one stretch of the export the read-ledger had marked unread,
   alongside two other things nobody knew were missing. `scripts/dc-diff.py`
   exists so that stops happening; run it after every re-export.

   OUTSIDE EVERY ZONED SECTION, deliberately. The pill is a child of body, so no
   descendant [data-zone] rule can ever reach it - it carries its own data-dark
   flag instead, written by frameLoop() from the same computed crossover the
   masthead and banner use. The DC hardcodes three section names for this; ours
   must not (Trap 2), and could not anyway - the crossover now anchors to
   [data-duskstart] and lands near 0.44 on a phone against 0.68 on a desktop.

   ⚠️ SAFE-AREA CLEARANCE IS UNVERIFIED ON HARDWARE. Headless Chrome resolves
   env(safe-area-inset-bottom) and -right to 0px, so both max() calls fall
   through to 22px here and measure "22px clear" whatever the device would do.
   On an iPhone 14/15 the bottom inset is 34px and the pill should sit there
   instead. A real handset reading is still owed - same caveat as .foot above.
   46px already clears the 44px --hit, so it needs no coarse-pointer rule. */
.totop { position:fixed; z-index:139;
  right:max(22px, env(safe-area-inset-right));
  bottom:max(22px, env(safe-area-inset-bottom));
  width:46px; height:46px; border-radius:50%;
  display:grid; place-items:center; font-size:17px; line-height:1;
  text-decoration:none;
  background:rgba(255,255,255,.55);
  backdrop-filter:blur(20px) saturate(180%);
  -webkit-backdrop-filter:blur(20px) saturate(180%);
  border:.5px solid rgba(255,255,255,.72); color:var(--purple-deep);
  box-shadow:0 12px 26px -10px rgba(36,0,74,.28);
  animation:fadeUp .45s var(--ease) both;
  transition:transform .3s var(--ease), box-shadow .3s var(--ease); }
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) {
  .totop { background:#fff; }
}
.totop:hover { transform:translateY(-2px); box-shadow:0 18px 34px -12px rgba(36,0,74,.34); }
.totop:active { transform:scale(.96); }
.totop[data-dark="true"] { background:rgba(29,11,56,.7);
  border-color:rgba(227,193,91,.3); color:var(--gold-old); }
@media (prefers-reduced-motion:reduce) {
  .totop { animation:none !important; transition:none !important; }
  .totop:hover { transform:none !important; }
}

/* The page behind the sheet is locked on <html>, not on body: html is this
   document's scrollingElement, so locking body alone leaves the page scrolling
   underneath. The scrim is position:fixed with its own overflow-y, so a long
   form still scrolls. */
html.portal-open, html.portal-open body { overflow:hidden; }
