@charset "UTF-8";
/* Academic Calendar — Template CSS */

/*
   No Elementor. This template was migrated from an Elementor page and carried
   its scaffolding: nine nested wrapper divs, a copy of the site's Elementor kit
   inlined at the top of this file, and six rules scoped to .elementor-99002.
   All of it is gone — the layout is two plain wrappers styled here, on top of
   what .int-page already provides (max-width, inline padding, font family and
   the heading scale).

   Structure follows int-mission-and-vision, the closest non-Elementor sibling:
   template class carries the top offset, then a hero, then a body.
*/

.academic-calendar {
  margin-bottom: 60px;
}

/* ── Hero ── */

/*
   Full-bleed, which is why it sits outside .int-page in the markup rather than
   inside it: that wrapper caps width at --content-max-width and adds 20px of
   inline padding, neither of which a viewport-wide band wants. No top margin —
   it butts directly against the subheader. The old 110px offset on the template
   class went with it: the subheader is position:sticky, and sticky elements stay
   in flow, so nothing needed clearing.

   The subheader is painted --color-primary-purple, hsl(269 53% 27%) = #442069,
   and this band is #42196F — 1.04:1 apart, so the two need something between
   them or they read as a single block. That something is a shadow cast by the
   subheader; see the rule below the hero.

   The gradient is centred rather than offset: unlike the semester bands, whose
   glow tracks a left-aligned title, this hero is centre-aligned.

   Being outside .int-page also means `.int-page h1` (0,1,1) no longer applies,
   so the title needs none of the specificity scoping it did before — the plain
   global `h1` rule is only (0,0,1).
*/
.acc-hero {
  padding: 138px 20px;
  background-color: #42196F;
  background-image: radial-gradient(
    900px 380px at 50% 50%,
    rgba(255, 255, 255, 0.13),
    rgba(255, 255, 255, 0.05) 55%,
    rgba(255, 255, 255, 0) 100%
  );
  font-family: var(--font-family);
  text-align: center;
}

.acc-hero__title {
  max-width: var(--content-max-width);
  margin: 0 auto;
  color: #fff;
  font-size: 2.5rem;
  line-height: 1.2;
}

.acc-hero__sub {
  max-width: var(--content-max-width);
  margin: 10px auto 0;
  color: rgba(255, 255, 255, 0.82);
  font-size: 1.15rem;
  line-height: 1.4;
}

/*
   The subheader is rendered by get_header(), outside this template, so shadowing
   it needs a page-scoped selector. :has() keys off our own hero rather than
   WordPress's body_class() output — `page-template-academic-calendar` would
   also work, but that string is derived from this file's path and would silently
   stop matching if the template ever moved.

   A hard-edged box-shadow rather than a border-bottom, though it renders
   identically. The subheader has a fixed 60px height and no border-box, so a
   real border would grow it to 65px — and its dropdown is positioned at
   `top: var(--subheader-height)`, so the menu would then open across the border.
   A shadow takes no layout space, and because the subheader is sticky it travels
   with it: the divide stays put as the page scrolls under it.

   Three layers in one declaration: a hard 1px line for the edge, then two soft
   ones for depth beneath it. The line does the dividing — purple on purple cannot
   be high-contrast, and --color-primary-deep-purple is 1.27:1 against the
   subheader, so #240b42 (1.38:1) or #1a0830 (1.48:1) go deeper if it reads soft.
   The soft layers cannot carry it alone: they fall on an already-dark hero, where
   a dark shadow has little to darken.
*/
body:has(.acc-hero) .subheader {
  box-shadow: 0 1px 0 #2d0f52,
              0 4px 8px rgba(0, 0, 0, 0.2),
              0 10px 20px rgba(0, 0, 0, 0.12);
}

/* ── Page body ── */

/*
   Pinned to the bottom-left corner, so the action stays reachable however far down
   the calendar the reader has scrolled instead of scrolling away with the header.

   Bottom-left rather than bottom-right because the Shell 451 chat widget occupies
   the bottom-right at z-index 2147483640 — nothing sensible competes with that, so
   the other corner is the one that is actually free.

   Only the top and right edges are ever on screen, so only those get a border;
   without one the light grey has no boundary against white content. The panel
   itself does not scroll away, so it needs a stacking order above the page but
   nowhere near the widget's.

   It carries no padding of its own. That lives on the button, so the button's box
   fills the rectangle and every grey pixel is inside the click target — padding
   here would have left a dead border around it that still looked pressable.
*/
.acc-print-row {
  position: fixed;
  bottom: 0;
  left: 0;
  z-index: 20;
  display: flex;
  border-top: 1px solid hsl(0 0% 78% / 1);
  border-right: 1px solid hsl(0 0% 78% / 1);
  background: hsl(0 0% 93% / 1);
}

/*
   .int-page already centres and caps the page at --content-max-width and adds
   20px of inline padding; this narrows the calendar itself and adds the vertical
   rhythm the old Elementor container supplied.
*/
.acc-body {
  max-width: 1060px;
  margin-inline: auto;
  padding: 48px 16px 60px;
}

/* ── View switch ── */

/*
   The radios stay in the DOM for keyboard and screen-reader support — a native
   radio group gives arrow-key navigation and "1 of 2, selected" for free — and
   are visually replaced by the labels below.
*/
.acc__view-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
   Plain text, not a pill: it shared the options' pill shape and their exact
   background, which made it read as a third, already-chosen option. Size and
   letter-spacing mark it as a label instead, and it is a <span> with no `for`
   target, so nothing about it is clickable either.
*/
.acc__views-label {
  display: inline-block;
  margin-bottom: 9px;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: hsl(0 0% 42% / 1);
}

/*
   Switch and search share this row. .acc__views below stays a grid of its own,
   which is what keeps the two pills equal width, so a third item could not simply
   join it.
*/
.acc__controls {
  display: flex;
  flex-wrap: wrap;
  /*
     flex-end, so the search field's bottom lands level with the pills'. The two
     columns are not the same height and cannot be: the left one carries the
     kicker above its pills, making it ~65px against the field's ~41px, so
     centring floats the field 12px clear of the pills' baseline.
  */
  align-items: flex-end;
  gap: 12px 16px;
  margin-bottom: 30px;
}

/*
   The kicker travels with the pills. It sat above .acc__controls before, which
   meant any change to the row's height moved the pills relative to it — adding the
   print link above the search made the right column taller and opened a gap.
   Inside the column, the two keep a fixed 9px whatever the other side does.
*/
.acc__views-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/*
   Two 1fr columns, not flex: a shrink-to-fit grid sizes flexible tracks from their
   max-content contributions and gives both the same fraction, so the pair ends up
   as wide as the longer label and no wider. Flex would need `flex: 1`, which
   stretches them across the whole row instead.

   `display: grid` with width: fit-content rather than `inline-grid` — the latter
   is inline-level, which put it on the same line as the inline-block kicker above.
   Block-level claims its own line; fit-content keeps it hugging the pills. On
   mobile the width goes to 100% so the two split the container instead.
*/
.acc__views {
  display: grid;
  width: fit-content;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: center;
  gap: 8px 9px;
}

/*
   Pills, matching the Semesters kicker above them. Unselected borrows that pill's
   exact palette — hsl(0 0% 93%) behind hsl(0 0% 42%) text, 4.55:1 — so the two
   read as one family. Selected inverts to brand purple at 13.12:1.

   Filling the unselected pill also retires a contrast problem rather than working
   around it: as bare text it sat at 1.41:1 on white, under the 3:1 these controls
   need even counting as large text.
*/
.acc__view {
  /* 1px more below than above: the labels have no descenders, so their ink sits
     high in the line box and equal padding reads bottom-light. */
  padding: 8px 32px 9px;
  /* Both pills are now the width of the longer label, so the shorter one's text
     needs centring or it would sit left in a pill wider than itself. */
  text-align: center;
  border-radius: 999px;
  background: hsl(0 0% 93% / 1);
  color: hsl(0 0% 42% / 1);
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0;
  cursor: pointer;
  /* The selected state is not transitioned — it is a page state, not a pointer
     response, and easing it would lag the content swap beneath it. */
  transition: background-color 0.18s ease, color 0.18s ease;
}

.acc__view:hover {
  background: hsl(0 0% 88% / 1);
  color: #42196F;
}

#acc-view-upcoming:checked ~ .acc__controls [for="acc-view-upcoming"],
#acc-view-past:checked ~ .acc__controls [for="acc-view-past"] {
  background: #42196F;
  color: #fff;
}

/* The hidden input cannot show focus itself, so the label carries it. */
#acc-view-upcoming:focus-visible ~ .acc__controls [for="acc-view-upcoming"],
#acc-view-past:focus-visible ~ .acc__controls [for="acc-view-past"] {
  outline: 2px solid #253F8E;
  outline-offset: 3px;
}

/* Only the checked view's group is rendered. */
.acc__group {
  display: none;
}

/*
   Descends through .acc__groups: the groups are wrapped so the whole set can be
   hidden while searching, which puts them one level below the radios rather than
   beside them.
*/
#acc-view-upcoming:checked ~ .acc__groups .acc__group--upcoming,
#acc-view-past:checked ~ .acc__groups .acc__group--past {
  display: block;
}

/*
   A text button, not a pill: the pills are a two-way choice about what is on
   screen, this is an action, and giving it their shape would put it in that set.
   Smaller too, so it reads as secondary to them.
*/
.acc__print {
  /* A flex row so the label and the icon after it share a line and centre on
     each other rather than sitting on the text baseline. */
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  /* The rectangle's padding, moved here off the wrapper so the whole of it is
     clickable rather than just the text. A px off the right because the icon ends
     that side and carries its own whitespace, which reads as a wider gap than the
     label's edge does on the left. */
  padding: 9px 8px 9px 9px;
  border: 0;
  background: none;
  /*
     Darker than the 46% it was. That figure was the lightest tone clearing 4.5:1
     against white, but the panel behind it is now 93% grey, where 46% falls to
     3.92:1. 40% restores the margin at 4.91:1 on the grey.
  */
  color: hsl(0 0% 40% / 1);
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 400;
  cursor: pointer;
}

/* Stroked in currentColor by the icon set, so it tracks the label's colour and
   its hover without a rule of its own. */
.acc__print .w-icon {
  flex: none;
}

/*
   The whole rectangle is the target, so hover darkens it rather than recolouring
   the text — a colour shift on a few words is a weak signal for a box this size.

   Both values move together on purpose. 88% grey was the first choice, but the
   40% label only manages 4.37:1 on it; taking the panel to 88% and the text to
   35% gives a clearly darker rectangle and 5.31:1.
*/
.acc__print:hover {
  background: hsl(0 0% 88% / 1);
  color: hsl(0 0% 35% / 1);
}

/*
   Drawn inside the button, not outside it. The rectangle sits flush in the corner,
   so an outward offset would be clipped along the bottom and left edges — exactly
   the two that are hardest to notice missing.
*/
.acc__print:focus-visible {
  outline: 2px solid #253F8E;
  outline-offset: -3px;
}

/* ── Text filter ── */

/*
   Narrower than it was: inline beside the pills it only needs room for its
   placeholder, not the width of the content column.

   margin-left: auto pushes it to the right edge. Put on the search rather than
   using justify-content on the row, so the pills stay left-aligned when the row
   wraps on a narrow screen instead of both ends drifting.
*/
.acc__search {
  flex: 0 1 270px;
  min-width: 0;
  margin-left: auto;
}

/*
   Off-screen but still read: the field's only visible naming is its placeholder,
   which assistive tech does not reliably treat as a label, and the status needs to
   be announced without duplicating the count the results header already shows.
*/
.acc__sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.acc__search-field {
  display: flex;
  align-items: stretch;
  gap: 8px;
  max-width: 420px;
}

/*
   appearance:none strips the UA search styling — Safari otherwise rounds the
   field and draws its own clear affordance, which would sit beside ours.
*/
.acc__search-input {
  flex: 1 1 auto;
  min-width: 0;
  appearance: none;
  /*
     9px lands the field at ~41.0px against the pills' ~41.3px — near enough to
     read as level, and derived from the box model rather than a hard height, so
     it tracks if either font-size changes. (Both figures are at the theme's 17px
     root; 1rem is not 16px here.)
  */
  padding: 9px 14px;
  border: 1px solid hsl(0 0% 85% / 1);
  border-radius: 6px;
  background: #fff;
  color: hsl(0 0% 20% / 1);
  font-family: inherit;
  font-size: 0.95rem;
  line-height: 1.3;
}

.acc__search-input::placeholder {
  /* 4.61:1 — placeholders are text and get held to the same floor as any other. */
  color: hsl(0 0% 46% / 1);
}

.acc__search-input:focus-visible {
  outline: 2px solid #253F8E;
  outline-offset: 1px;
  border-color: #253F8E;
}

.acc__search-clear {
  flex: none;
  padding: 9px 15px;
  border: 1px solid hsl(0 0% 85% / 1);
  border-radius: 6px;
  background: #fff;
  color: hsl(0 0% 42% / 1);
  font-family: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
}

.acc__search-clear:hover {
  border-color: #42196F;
  color: #42196F;
}

/*
   The results card is a .acc__term, so it inherits the card, the band, the chevron
   and the row rules already defined below — only these extras are new.
*/

/*
   The divider between tenses, drawn inside the one pane rather than between two.
   Dashed rather than solid so it reads differently from the row separators, which
   are solid hsl(0 0% 92%) — a solid grey line here would look like one more row
   rule rather than a break between sections. Black carries that further.
*/
.acc__results-section + .acc__results-section {
  margin-top: 4px;
  border-top: 1px dashed #000;
}

/*
   Pills, so the tense labels read as labels rather than as headings competing with
   the band above. inline-block is what lets them hug their text — as a block <p>
   the fill would span the pane.

   The 28px is a margin now, not padding: padding would sit inside the fill and
   make the pill 28px wider than its text.
*/
.acc__results-heading {
  display: inline-block;
  /* Bottom bias dropped with the caps: these labels have descenders now
     ("Current & Future"), so their ink already sits low and equal padding reads
     level. */
  margin: 13px 0 7px 28px;
  padding: 6px 14px;
  border-radius: 999px;
  background: hsl(0 0% 93% / 1);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: hsl(0 0% 42% / 1);
}

/*
   A following section's pill needs more air above it than the first, which sits
   directly under the band. Safe as a margin here because an inline-block's
   vertical margins do not collapse through the section boundary — on the old
   block heading this had to be padding for that reason.
*/
.acc__results-section + .acc__results-section .acc__results-heading {
  margin-top: 26px;
  margin-bottom: 6px;
}

.acc__results-empty {
  margin: 0;
  padding: 22px 28px 26px;
  color: hsl(0 0% 42% / 1);
  font-size: 0.9rem;
}

/* Which semester a result came from — "May 7" alone is ambiguous across years. */
.acc__row-term {
  margin-left: 9px;
  /* 45% is the lightest this goes and still clears 4.5:1 — at 0.8rem it is normal
     text, not large, so the 3:1 allowance does not apply. */
  color: hsl(0 0% 45% / 1);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
}

/* ── Semester sections ── */

/*
   --acc-season drives the header band. Set on the term so the head, its hover
   state and its focus ring all read the one value. Each colour is chosen to
   clear AA for white text; hover darkens rather than lightens, which can only
   improve that (a lightening hover put the gold at 3.6:1).
*/
.acc__term {
  --acc-season: #42196F;
  margin-top: 46px;
  border: 1px solid hsl(0 0% 92% / 1);
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 3px 10px rgba(0, 0, 0, 0.045);
  /*
     Clips the header band to the card's rounded corners. The band is square and
     spans the full width, so without this its corners would sit outside the
     radius. Its own shadow falls downward onto the rows, inside the card, so
     nothing wanted is lost.
  */
  overflow: hidden;
}

.acc__term--spring { --acc-season: #1B6B4A; }
.acc__term--summer { --acc-season: #8F6300; }
.acc__term--fall   { --acc-season: #A34419; }
.acc__term--winter { --acc-season: #1F3A6E; }

/* No leading gap above the first semester. */
.acc__term:first-child {
  margin-top: 8px;
}

/*
   A solid purple band rather than an underline. align-items is center rather
   than baseline because the chevron below is an empty pseudo-element, which
   has no baseline of its own to align to.

   Every term head is a <summary>, so the disclosure styling lives here rather
   than in a separate summary-qualified rule.
*/
.acc__term-head {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 6px;
  padding: 18px 18px 18px 28px;
  /*
     Season colour as background-color, glow as background-image — kept separate
     so :hover only has to swap the colour and the gradient carries through
     untouched. An ellipse anchored 130px from the left edge rather than at a
     percentage, so it stays centred on the title regardless of band width.
  */
  background-color: var(--acc-season);
  background-image: radial-gradient(
    340px 150px at 130px 50%,
    rgba(255, 255, 255, 0.2),
    rgba(255, 255, 255, 0.07) 55%,
    rgba(255, 255, 255, 0) 100%
  );
  /*
     Two layers cast straight down onto the rows: a tight dark one right under
     the band's edge for a defined line, and a softer one for falloff. Neutral
     black rather than a tinted shadow, so it reads the same under all four
     seasonal colours. Not transitioned — only background-color is, below.
  */
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.17),
              0 5px 9px rgba(0, 0, 0, 0.09);
  color: #fff;
  font-size: 1.4rem;
  line-height: 1.3;
  cursor: pointer;
  list-style: none;
  /*
     background-color only. The glow lives in background-image, which is not
     animatable — that split was made so :hover could swap the colour without
     losing the gradient, and it pays off again here. The chevron stays
     deliberately untransitioned.
  */
  transition: background-color 0.18s ease;
}

/*
   In a past term this is an <h2> inside the summary, which the theme's
   `.int-page h2` rule (0,1,1) styles with a dark colour, 2rem size and 2.5rem
   margins. Scoping through .acc__term-head takes this to 0,2,0 so the reset
   actually applies — without it, past headings alone came out dark, oversized
   and spaced differently from the rest.
*/
.acc__term-head .acc__term-name {
  /* auto on the right absorbs the free space, pushing badge + chevron right. */
  margin: 0 auto 0 0;
  font-size: inherit;
  font-weight: 700;
  color: #fff;
  letter-spacing: 0.01em;
}

.acc__term-head::-webkit-details-marker {
  display: none;
}

/*
   Chevron replacing the default triangle. No auto margin of its own — the
   title's margin-right does the pushing, so the chevron simply trails the badge
   at the right end whether or not a term carries one.
*/
.acc__term-head::after {
  content: "";
  flex: none;
  margin: 0 3px 4px 0;
  width: 9px;
  height: 9px;
  border-right: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(45deg);
}

.acc__term[open] > .acc__term-head::after {
  transform: rotate(-135deg);
  margin: 4px 3px 0 0;
}

/*
   Hover darkens the band; the easing is declared on .acc__term-head above.
*/
.acc__term-head:hover {
  background-color: color-mix(in srgb, var(--acc-season) 85%, #000);
}

.acc__term-head:focus-visible {
  outline: 2px solid var(--acc-season);
  outline-offset: 3px;
}

/*
   The band's 6px gap exists to separate it from the first row. A closed card
   renders no rows, so that margin showed up as a strip of card background
   between the band and the card's bottom border.
*/
.acc__term:not([open]) > .acc__term-head {
  margin-bottom: 0;
}

/*
   Plain text, no fill and no outline, so the white sits directly on the band and
   inherits its contrast (5.3:1 at worst, on the gold). Nothing to re-check per
   season, and nothing that shifts when hover darkens the band.
*/
.acc__term-tag {
  /* On top of the head's 9px gap, so ~21px sits between badge and chevron. */
  margin-right: 12px;
  padding: 8px 0 6px;
  background: transparent;
  color: #fff;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

/* ── Date rows ── */

.acc__list {
  margin: 0;
  padding: 0 0 6px;
  list-style: none;
}

/*
   Each row is its own grid, so the date column has to be a fixed width —
   `auto` would size per row and the dates would not line up down the page.
   176px is the widest the date cell gets: a 68px "Mon–Fri" weekday beside a
   cross-month range like "Dec 25–Jan 2", neither of which wraps.
*/
.acc__row {
  display: grid;
  grid-template-columns: 176px 1fr auto;
  align-items: center;
  column-gap: 12px;
  /*
     Padding rather than insetting .acc__list, so the row background and its
     separator still run the full width of the card while the text is held off
     the border.
  */
  padding: 0 18px 0 28px;
  min-height: 40px;
  border-bottom: 1px solid hsl(0 0% 92% / 1);
  font-size: 0.9rem;
}

.acc__row:last-child {
  border-bottom: 0;
}

/*
   Two fixed sub-columns rather than a flex row, so weekdays and dates each
   align down the page instead of shifting with the width of the one before.
*/
.acc__date {
  display: grid;
  grid-template-columns: 68px 1fr;
  align-items: baseline;
  gap: 0 8px;
  padding: 16px 0;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.acc__date-day {
  font-size: 0.72rem;
  color: #9a94a6;
}

.acc__date-num {
  font-weight: 600;
  color: #4a4553;
}

.acc__title {
  padding: 16px 0;
  color: #333;
  line-height: 1.35;
}

.acc__cat {
  padding: 16px 0;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  opacity: 0.85;
}

/*
   The dash is generated, not written into the markup. As a text node beside the
   span it would survive `display: none` on the label, leaving a stray " — " in
   front of the description on printouts.

   It reads as punctuation joining the label to the description, so it takes the
   description's colour and weight rather than the label's — being generated by
   the label it would otherwise inherit the bold colour.
*/
.acc__next::after,
.acc__now::after {
  /*
     Escaped, not the literal character. This file is served as `text/css` with no
     charset parameter and declares none itself, and CSS then falls back to the
     referring document's encoding before UTF-8 — so the em dash's three bytes were
     only decoded correctly when that guess happened to land, which is why it came
     out wrong until a hard refresh. \2014 is the codepoint however the bytes read.

     \20 for the spaces rather than literal ones: a hex escape swallows one
     trailing space as its terminator, so " \2014 " would lose the space after the
     dash. white-space: pre below keeps both.
  */
  content: "\20\2014\20";
  white-space: pre;
  color: #333;
  font-weight: 400;
}

/*
   Inline label at the title's own size, so it reads as the start of the sentence
   rather than an object beside it. Deliberately plain text with no box: an
   inline-block here would be an atomic inline, and a line box is sized from its
   margin box, so any badge taller than the title's 19.4px line box would leave
   this single row taller than the other 35.
*/
.acc__next {
  font-weight: 700;
  /*
     cornflowerblue as named is #6495ED, which is 2.97:1 on white — under the
     4.5:1 this needs at 0.9rem. #3B6CD4 is the same hue taken dark enough to
     clear it at 4.90:1.
  */
  color: #3B6CD4;
}

/*
   Same inline treatment as .acc__next above, and for the same layout reason — it
   sits in the title's line box, so it must not be an atomic inline taller than
   the title. Brand purple rather than the blue: #42196F is 12.6:1 on white.
*/
.acc__now {
  font-weight: 700;
  color: #42196F;
}

/*
   Closure days and commencement get a pale wash so they read as blocks at a
   glance. Each is roughly 6% of its brand hue on white. Text colour is
   untouched — only the background carries the signal.
*/
.acc__row[data-cat="holiday"] {
  background: #f0f7f4;
}

.acc__row[data-cat="commencement"] {
  background: #f4f1f6;
}

/* ── Narrow screens ── */

/*
   `screen and` is deliberate. Without a media type this would apply to print too,
   and while A4 at 96dpi is ~794px so it would not usually trigger, paper size and
   scaling can put the page box under 700px — the print layout should not depend
   on that.
*/
@media screen and (max-width: 700px) {

  /*
     Reclaim horizontal space. Text sat 57px from the viewport edge — .int-page's
     20px, .acc-body's 16px, the card border and the row's 20px all stacking. The
     page padding drops to 12px and .acc-body's goes entirely, leaving the row to
     own the inset.
  */
  .int-page.academic-calendar {
    padding-inline: 12px;
  }

  /*
     The top padding is the whole gap between the hero and the kicker: the print
     row in between is display:none here, so it adds nothing. 30px matches the
     gap under the search field, which is .acc__controls' 30px bottom margin
     winning a collapse against the first semester's 8px top margin.
  */
  .acc-body {
    padding: 30px 0 40px;
  }

  .acc-hero {
    padding: 96px 20px;
  }

  /* Full width, and below the pills rather than beside them. */
  .acc__search {
    flex: 1 1 100%;
    margin-left: 0;
  }

  /*
     No print control on phones. Printing from a phone is a rare enough thing that
     the button is not worth the row it costs here — and the reader who does want
     it still has the browser's own share/print menu, which is what they would
     reach for on a phone anyway.
  */
  .acc-print-row {
    display: none;
  }

  /*
     The pills fill the row and split it evenly. width:100% replaces fit-content,
     so the two 1fr columns divide the container instead of the longer label — and
     the side padding comes right down, since the pills no longer need to create
     their own width.
  */
  .acc__views {
    width: 100%;
  }

  /*
     Smaller text in the same pill. The line-height goes up as the font-size comes
     down so the line box stays 22.8px against the 22.88px it was, holding the
     height without changing the vertical padding.

     The wider sides are breathing room rather than width: the two pills are 1fr
     columns of a full-width grid here, so the container sizes them and their text
     never does. Past about 19px a side the longer label would wrap and the pills
     would grow taller instead.
  */
  .acc__view {
    padding: 8px 16px 9px;
    font-size: 0.95rem;
    line-height: 1.5;
  }

  .acc__term {
    margin-top: 26px;
  }

  .acc__term-head {
    padding: 16px 14px 16px 16px;
    font-size: 1.1rem;
  }

  /* Scaled with the band it sits in — a 5px border reads heavy at this size. */
  .acc__term-tag {
    margin-right: 8px;
    padding: 6px 0 5px;
    font-size: 0.68rem;
  }

  /*
     Two columns, two rows:

       day    description
       date   tag

     The date column has a floor of 72px rather than sizing to content, so every
     row aligns down the page. The widest string the data actually produces is
     "Dec 25–27" at roughly 65px; minmax and not a fixed width because
     acc_range_label can emit a cross-month range like "Dec 25–Jan 2", which would
     land past the floor and needs to grow rather than overflow. The description
     takes the remaining 1fr.

     align-items stays start. It cannot be baseline: .acc__date spans two rows, and
     a grid item spanning more than one track in the relevant axis is excluded from
     baseline alignment and falls back to start anyway. The weekday is nudged onto
     the description's baseline by hand instead — see .acc__date below.
  */
  .acc__row {
    grid-template-columns: minmax(72px, auto) 1fr;
    column-gap: 12px;
    row-gap: 3px;
    padding: 10px 11px 12px;
    align-items: start;
  }

  /*
     Spans both rows and stacks internally, so the day sits over the date without
     either needing to be a grid item of the row itself. Doing it that way would
     mean display: contents on the <time>, which drops the element from the
     accessibility tree along with its datetime attribute.
  */
  .acc__date {
    grid-column: 1;
    grid-row: 1 / span 2;
    grid-template-columns: 1fr;
    row-gap: 3px;
    justify-items: start;
    /*
       3px down, to sit the weekday on the description's baseline beside it. The
       two cannot be aligned by the grid: this element spans two rows, which
       excludes it from baseline alignment, so the offset is applied here.

       Where 3px comes from: the description is 15.3px on a 1.35 line-height, so
       its first baseline falls 14.9px below the top of its box; the weekday is
       11.6px on the inherited 1.4, putting its baseline at 11.6px. The 3.4px
       difference is the correction. It follows from the two font sizes, so it
       needs revisiting if either changes.
    */
    padding: 3px 0 0;
  }

  /*
     Stacked, so they no longer read as one phrase and can be told apart: the
     weekday recedes, the date leads.

     Not the #9a94a6 the weekday uses at full width — that is 2.93:1 on white,
     under the 4.5:1 this size needs, and it was only ever defensible there as a
     supporting note beside a dark date. #6f6a7b is 5.22:1 and still plainly
     lighter than the date's #5a5465 at 7.26:1.
  */
  .acc__date-day {
    font-size: 0.68rem;
    font-weight: 400;
    color: #6f6a7b;
  }

  .acc__date-num {
    font-size: 0.82rem;
    font-weight: 600;
    /* Lighter than the #4a4553 used at full width, but still clearly ahead of the
       weekday above it — 7.26:1 against the weekday's 5.22:1. */
    color: #5a5465;
  }

  /* Under the description, left-aligned with it rather than pushed to the edge. */
  .acc__cat {
    grid-column: 2;
    grid-row: 2;
    justify-self: start;
    padding: 0;
    font-size: 0.78rem;
  }

  /*
     Top-right cell. No padding of its own: it used to carry the gap that separated
     it from the date line above, and now that the two sit side by side that gap
     would only push it out of line with the date. The grid's row-gap handles the
     space to the tag beneath.
  */
  .acc__title {
    grid-column: 2;
    grid-row: 1;
    padding: 0;
  }

  /* Indents follow the row's, which came in from 28px. */
  .acc__results-heading {
    margin-left: 11px;
  }

  .acc__results-empty {
    padding: 18px 11px 22px;
  }
}

/* ── Print ── */

/*
   Narrower sides than top and bottom, which buys 8mm of usable width per edge for
   the date rows. The top and bottom stay at 12mm: the masthead is a fixed element
   painted into the top margin area on every sheet, and the rows need somewhere to
   breathe above the paper's edge.

   A floor exists that is not ours. Browsers clamp @page to the printer's printable
   area, and the print dialog's own margin setting overrides this outright — so 8mm
   is a request, not a guarantee, and roughly 5-6mm is as tight as most hardware
   will physically go.
*/
@page {
  margin: 12mm 8mm;
}

@media print {

  /*
     Site chrome. Scoped through body:has(.acc-hero) so this stylesheet cannot
     reach another page if it is ever loaded more broadly.
  */
  body:has(.acc-hero) .site-header,
  body:has(.acc-hero) .subheader,
  body:has(.acc-hero) footer {
    display: none;
  }

  /* Controls do nothing on paper. */
  .acc__controls,
  .acc-print-row {
    display: none;
  }

  /*
     The Shell 451 chat widget, injected by wnmu_add_shell451_chat() in
     functions.php. The script builds one host element on the page —

       <div id="messenger-root" style="position:fixed;bottom:0;right:0;…">
         <iframe src="https://wnmu.messenger.451.io/…">

     — and everything else, launcher button and its drop shadow included, lives
     inside that cross-origin iframe where no rule of ours can reach. Hiding the
     host is the only lever, and it takes the iframe with it.

     Depends on the vendor's id. If they rename it the widget comes back on paper,
     which is the trade for being able to target it at all.

     !important is load-bearing, not habit: the host is styled inline, so an
     inline display would beat any rule without it.
  */
  body:has(.acc-hero) #messenger-root {
    display: none !important;
  }

  /*
     "Upcoming" and "Today" are relative to the day the page was printed and paper
     cannot keep up, so they go — along with their generated dash, which is why
     that lives in ::after rather than in the markup.
  */
  .acc__next,
  .acc__now {
    display: none;
  }

  /*
     The masthead repeats on every sheet. A fixed element is laid out against the
     page box rather than the document, so browsers paint it once per page — the
     only way to get a running header without table markup.

     It follows that flow content does not know to avoid it: each semester carries
     its own top padding to clear it. That works because every semester starts a
     new page below, so no page begins mid-term.
  */
  .acc-hero {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    padding: 0;
    background: none;
    text-align: left;
  }

  .acc-hero__title {
    margin: 0;
    color: #000;
    font-size: 17pt;
    line-height: 1.15;
  }

  .acc-hero__sub {
    margin: 1mm 0 0;
    color: #333;
    font-size: 9.5pt;
    line-height: 1.2;
  }

  /*
     Full bleed to the page margins: .int-page's inline padding is screen sizing
     and would otherwise inset every band from the paper's edge.
  */
  .int-page.academic-calendar,
  .acc-body {
    max-width: none;
    margin: 0;
    padding: 0;
  }

  /*
     One semester per sheet, and 17mm of clearance for the running masthead. The
     first is not excepted — it needs the same clearance, it just has no break
     before it.
  */
  .acc__term {
    /*
       Greyscale. The season colours collapse to one grey through --acc-season:
       the head reads the variable, so a single declaration recolours all four.
       It matches the season variants' specificity and comes later in source, so
       it wins.

       Explicit greys rather than filter: grayscale(1) on a wrapper, because a
       filter makes its element a containing block for fixed-position descendants —
       which would drop the running masthead above back into the flow and print it
       once, at the top of page one, instead of on every sheet.
    */
    --acc-season: #4a4a4a;
    margin-top: 0;
    padding-top: 17mm;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    break-before: page;
    break-inside: auto;
  }

  .acc__term:first-child {
    break-before: auto;
  }

  /* Flush to the page margins, so the band spans the full printable width. */
  .acc__term-head {
    margin-bottom: 2mm;
    padding: 2.5mm 3mm;
    background-image: none;
    box-shadow: none;
    font-size: 13pt;
    break-after: avoid;
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }

  .acc__row {
    padding: 2.4mm 3mm;
    min-height: 0;
    font-size: 10pt;
    break-inside: avoid;
  }

  /*
     Weekday and date identical, and neither bolded.

     The column is wider than the screen's looks like it needs because matching the
     two sizes grew the weekday from 0.72rem to the full 10pt — enough for a range
     like "Mon–Thu" to run into the date beside it.
  */
  .acc__date {
    grid-template-columns: 17mm 1fr;
    padding: 0;
  }

  .acc__date-day,
  .acc__date-num {
    font-size: inherit;
    font-weight: 400;
    color: #000;
  }

  .acc__title,
  .acc__cat {
    padding: 0;
  }

  /* The colour already sets the tag apart on paper; bold on top of it shouts. */
  .acc__cat {
    font-weight: 400;
  }

  /*
     Row washes are structure rather than decoration — worth the ink. Holiday and
     commencement share one grey: distinguishing them would mean two greys a few
     percent apart, which reads as a printing artefact rather than a distinction.
     The tag at the end of the row is what names the category.
  */
  .acc__row[data-cat="holiday"],
  .acc__row[data-cat="commencement"] {
    background: #f0f0f0;
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }

  /*
     The one place !important is unavoidable: the category colour is an inline
     style attribute written by index.php from $acc_cats, and inline styles beat
     any rule without it.
  */
  .acc__cat {
    color: #000 !important;
  }

  .acc__results-heading {
    break-after: avoid;
  }

  /* Screen-only affordance. */
  .acc__term-head::after {
    display: none;
  }
}
