/* ---------------------------------------------------------------
   Fakhir Shaheen — site styles
   Palettes lifted from the design spec's dark/lightT token sets.
   --------------------------------------------------------------- */

/* Dark is the default palette. */
:root {
  --bg: #0d0f12;
  --text: #d7d3cb;
  --strong: #f4f2ee;
  --muted: #a8a49b;
  --faint: #6b6f77;
  --line: #1e2126;
  --panel-line: #2a2d33;
  --panel-bg: #101318;
  --ghost-border: #3a3e45;
  --tag-border: #4a2c1d;
  --green: #3fb950;

  --hero-title: #f4f2ee;
  --hero-body: #c9c5bc;
  /* Lighter than the body accent: at 13px this line needs the extra
     luminance to clear 4.5:1 over the scrim. */
  --accent-on-hero: #ffa982;
  /* Flat black box rather than a gradient: the old gradient thinned out to
     0.25 alpha exactly where the headline sits, so the text merged into the
     render's bright horizon band. */
  --hero-scrim: rgba(0, 0, 0, 0.68);

  /* Alternating week band in the course calendar (dark: recede). */
  --wk-band: rgba(0, 0, 0, 0.22);

  /* Accent is palette-independent. */
  --accent: #ff6b35;
  --accent-hover: #ff8a5c;

  --maxw: 1080px;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "IBM Plex Sans", system-ui, -apple-system, Segoe UI, sans-serif;
}

/* Light palette. Applied when the user picks light, or when the system
   prefers light and the user has expressed no preference. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f7f5f1;
    --text: #3a3733;
    --strong: #1c1a17;
    --muted: #6e6a62;
    --faint: #98938a;
    --line: #e4e0d8;
    --panel-line: #d8d3c9;
    --panel-bg: #fdfcfa;
    --ghost-border: #c4bfb5;
    --tag-border: #f0c9b5;
    --green: #1a7f37;

    /* Hero text stays light in both themes — it sits over the render. */
    --hero-title: #ffffff;
    --hero-body: #e8e5de;
    --accent-on-hero: #ffa982;
    --hero-scrim: rgba(0, 0, 0, 0.68);
    /* Light: a warm paper tint rather than a grey wash. */
    --wk-band: rgba(120, 100, 70, 0.055);
  }
}

:root[data-theme="light"] {
  --bg: #f7f5f1;
  --text: #3a3733;
  --strong: #1c1a17;
  --muted: #6e6a62;
  --faint: #98938a;
  --line: #e4e0d8;
  --panel-line: #d8d3c9;
  --panel-bg: #fdfcfa;
  --ghost-border: #c4bfb5;
  --tag-border: #f0c9b5;
  --green: #1a7f37;

  --hero-title: #ffffff;
  --hero-body: #e8e5de;
  --accent-on-hero: #ffa982;
  --hero-scrim: rgba(0, 0, 0, 0.68);
  --wk-band: rgba(120, 100, 70, 0.055);
}

/* --- base ------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  color: var(--accent-hover);
}

::selection {
  background: var(--accent);
  color: #ffffff;
}

/* Visible keyboard focus — the design has none, but shipping without it
   makes the site unusable without a mouse. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

h1,
h2,
h3 {
  color: var(--strong);
  letter-spacing: -0.02em;
}

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: 24px;
}

.mono {
  font-family: var(--mono);
}

/* Skip link for keyboard users. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--accent);
  color: #fff;
  padding: 10px 16px;
  font-size: 14px;
  font-weight: 600;
}

.skip:focus {
  left: 0;
  color: #fff;
}

/* --- nav -------------------------------------------------------- */

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 20px 24px;
  border-bottom: 1px solid var(--line);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  flex-shrink: 0;
}

.nav__brand:hover {
  color: var(--text);
}

.nav__mark {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 700;
  color: #ffffff;
  background: var(--accent);
  padding: 3px 6px;
  letter-spacing: 0.5px;
}

.nav__name {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.nav__links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 28px;
  font-size: 14px;
}

.nav__links a {
  color: var(--muted);
}

.nav__links a:hover {
  color: var(--strong);
}

/* Current page gets the accent underline. */
.nav__links a[aria-current="page"] {
  color: var(--strong);
  box-shadow: inset 0 -2px 0 var(--accent);
}

/* --- theme toggle ----------------------------------------------- */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--ghost-border);
  color: var(--muted);
  cursor: pointer;
  flex-shrink: 0;
}

.theme-toggle:hover {
  color: var(--strong);
  border-color: var(--muted);
}

.theme-toggle svg {
  width: 16px;
  height: 16px;
  display: block;
}

/* Show the icon for the theme you'd switch *to*. */
.theme-toggle .icon-sun {
  display: none;
}

:root[data-theme="light"] .theme-toggle .icon-sun {
  display: block;
}

:root[data-theme="light"] .theme-toggle .icon-moon {
  display: none;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle .icon-sun {
    display: block;
  }
  :root:not([data-theme="dark"]) .theme-toggle .icon-moon {
    display: none;
  }
}

/* --- hero ------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 560px;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
}

.hero__media {
  position: absolute;
  inset: 0;
  /* Clip any deliberate overflow (see the mobile crop rule) instead of
     letting the image spill past the hero. */
  overflow: hidden;
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.hero__scrim {
  position: absolute;
  inset: 0;
  background: var(--hero-scrim);
  pointer-events: none;
}

.hero__inner {
  position: relative;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 64px 24px 56px;
  /* The scrim swallows clicks; buttons re-enable them. */
  pointer-events: none;
}

.hero__kicker {
  margin: 0 0 14px;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--accent-on-hero);
  letter-spacing: 0.06em;
}

.hero__title {
  margin: 0 0 16px;
  font-size: clamp(38px, 6vw, 64px);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--hero-title);
  max-width: 15ch;
}

.hero__lede {
  margin: 0 0 28px;
  font-size: clamp(17px, 2.2vw, 21px);
  line-height: 1.5;
  color: var(--hero-body);
  max-width: 46ch;
  text-wrap: pretty;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  pointer-events: auto;
}

/* --- buttons ---------------------------------------------------- */

.btn {
  display: inline-block;
  background: var(--accent);
  color: #ffffff;
  font-size: 15px;
  font-weight: 600;
  padding: 12px 22px;
}

.btn:hover {
  background: var(--accent-hover);
  color: #ffffff;
}

.btn--ghost {
  background: transparent;
  color: var(--hero-title);
  font-weight: 500;
  padding: 11px 22px;
  border: 1px solid var(--ghost-border);
}

.btn--ghost:hover {
  background: transparent;
  border-color: var(--muted);
  color: var(--hero-title);
}

/* --- panel (the ~/now block) ------------------------------------ */

.panel {
  border: 1px solid var(--panel-line);
  background: var(--panel-bg);
}

.panel__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--panel-line);
  font-family: var(--mono);
  font-size: 12px;
}

.panel__path {
  color: var(--muted);
}

.panel__stamp {
  color: var(--faint);
}

.panel__body {
  padding: 20px 16px 22px;
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.9;
  display: grid;
  gap: 2px;
}

.row {
  display: flex;
  flex-wrap: wrap;
  gap: 0 10px;
}

.row__key {
  color: var(--accent);
}

.row__arrow {
  color: var(--faint);
}

.row__val {
  color: var(--strong);
}

.bench {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px dashed var(--panel-line);
  display: grid;
  gap: 4px;
}

.bench__row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 10px;
  font-size: 13px;
}

.bench__label {
  color: var(--muted);
  min-width: 200px;
}

.bench__val {
  color: var(--strong);
}

.bench__delta {
  color: var(--green);
}

.prompt {
  margin-top: 12px;
  color: var(--faint);
  font-size: 13px;
}

.cursor {
  display: inline-block;
  width: 8px;
  height: 15px;
  background: var(--accent);
  vertical-align: text-bottom;
  animation: blink 1.1s step-end infinite;
}

@keyframes blink {
  0%,
  49% {
    opacity: 1;
  }
  50%,
  100% {
    opacity: 0;
  }
}

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

/* --- sections --------------------------------------------------- */

.section {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 64px 24px;
}

.section--now {
  padding: 56px 24px 8px;
}

.section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 8px;
}

.section__head h2 {
  margin: 0;
  font-size: 24px;
  font-weight: 600;
}

.section__meta {
  display: flex;
  align-items: center;
  gap: 14px;
}

.section__count {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--faint);
}

.icon-link {
  display: inline-flex;
  color: var(--muted);
}

.icon-link:hover {
  color: var(--accent);
}

/* --- post list -------------------------------------------------- */

.posts {
  border-top: 1px solid var(--panel-line);
}

.post {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  padding: 24px 0;
  border-bottom: 1px solid var(--line);
  color: inherit;
  align-items: baseline;
}

.post:hover {
  background: var(--panel-bg);
  color: inherit;
}

.post__date {
  flex: 0 0 110px;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--faint);
}

.post__main {
  flex: 1 1 340px;
  min-width: 0;
  display: grid;
  gap: 6px;
}

.post__titleline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px;
}

.post__title {
  font-size: 19px;
  font-weight: 600;
  color: var(--strong);
  letter-spacing: -0.01em;
}

.tag {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--accent);
  border: 1px solid var(--tag-border);
  padding: 2px 7px;
}

.post__dek {
  font-size: 15px;
  color: var(--muted);
  line-height: 1.55;
  text-wrap: pretty;
}

/* --- page header (interior pages) -------------------------------- */

.pagehead {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 64px 24px 8px;
}

.pagehead__kicker {
  margin: 0 0 12px;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--accent);
  letter-spacing: 0.06em;
}

.pagehead h1 {
  margin: 0 0 16px;
  font-size: clamp(32px, 5vw, 46px);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.025em;
}

.pagehead__lede {
  margin: 0;
  font-size: clamp(16px, 2vw, 19px);
  line-height: 1.6;
  color: var(--muted);
  max-width: 58ch;
  text-wrap: pretty;
}

/* --- prose ------------------------------------------------------ */

.prose {
  max-width: 68ch;
}

.prose p {
  font-size: 16px;
  line-height: 1.7;
  color: var(--text);
  text-wrap: pretty;
}

.prose h2 {
  font-size: 21px;
  font-weight: 600;
  margin: 40px 0 12px;
}

.prose h3 {
  font-size: 17px;
  font-weight: 600;
  margin: 28px 0 8px;
}

.prose ul {
  padding-left: 20px;
  line-height: 1.7;
}

.prose li {
  margin-bottom: 8px;
}

.prose li::marker {
  color: var(--accent);
}

/* --- definition-style listings (courses, roles) ------------------ */

.entries {
  display: grid;
  gap: 0;
  border-top: 1px solid var(--panel-line);
}

.entry {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  padding: 24px 0;
  border-bottom: 1px solid var(--line);
  align-items: baseline;
}

.entry__side {
  flex: 0 0 110px;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--faint);
}

.entry__main {
  flex: 1 1 340px;
  min-width: 0;
  display: grid;
  gap: 6px;
}

.entry__title {
  font-size: 17px;
  font-weight: 600;
  color: var(--strong);
}

.entry__dek {
  font-size: 15px;
  color: var(--muted);
  line-height: 1.55;
  text-wrap: pretty;
}

/* Clickable entry — same row, but it links somewhere. */
a.entry {
  color: inherit;
}

a.entry:hover {
  background: var(--panel-bg);
  color: inherit;
}

a.entry:hover .entry__title {
  color: var(--accent);
}

.entry__term {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--accent);
  border: 1px solid var(--tag-border);
  padding: 2px 7px;
  white-space: nowrap;
}

.entry__titleline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px;
}

/* Course-page meta strip (term, code, level). */
.meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 28px;
  padding: 16px 0;
  margin-bottom: 8px;
  border-top: 1px solid var(--panel-line);
  border-bottom: 1px solid var(--panel-line);
  font-family: var(--mono);
  font-size: 13px;
}

.meta__item {
  display: flex;
  gap: 8px;
}

.meta__key {
  color: var(--faint);
}

.meta__val {
  color: var(--strong);
}

/* --- back link (end of an article) ------------------------------- */

.backlink {
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  padding: 24px 0;
  border-top: 1px solid var(--panel-line);
  width: 100%;
  font-size: 15px;
  font-weight: 600;
  color: var(--muted);
}

.backlink:hover {
  color: var(--accent);
}

.backlink__arrow {
  font-family: var(--mono);
}

/* --- course documents ------------------------------------------- */

.docs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 16px 18px;
  border: 1px solid var(--panel-line);
  background: var(--panel-bg);
}

.docs__note {
  margin: 12px 0 0;
  font-size: 14px;
  color: var(--muted);
  line-height: 1.6;
  max-width: 62ch;
  text-wrap: pretty;
}

/* Larger tap target than the inline calendar chips — these are the two
   documents most people come to the page for. */
.docs .pdf {
  font-size: 12px;
  padding: 7px 12px;
}

/* --- course calendar -------------------------------------------- */

/* Wide table: scrolls inside its own box so the page never scrolls
   horizontally on narrow screens. */
.calendar-scroll {
  overflow-x: auto;
  border: 1px solid var(--panel-line);
  background: var(--panel-bg);
}

.calendar {
  width: 100%;
  min-width: 660px;
  border-collapse: collapse;
  font-size: 14px;
}

.calendar th,
.calendar td {
  text-align: left;
  vertical-align: top;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
}

.calendar thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel-bg);
  border-bottom: 1px solid var(--panel-line);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}


.calendar .c-wk,
.calendar .c-lec {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--faint);
  white-space: nowrap;
}

.calendar .c-date {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}

.calendar .c-topic {
  color: var(--strong);
  min-width: 15rem;
}

/* Week banding: each week's pair of lectures shares a tint, and
   consecutive weeks alternate — so the grouping reads without a week
   column. --wk-band flips direction per theme (darker on dark, darker-
   but-warmer on light) so the effect is visible either way. */
.calendar tr.wk-odd td {
  background: var(--wk-band);
}

/* Keep the boundary rule, now only as a faint seam between weeks. */
.calendar tr.wk-start td {
  border-top: 1px solid var(--panel-line);
}

/* Exam / break rows. */
.calendar tr.row-exam td {
  background: color-mix(in srgb, var(--accent) 9%, transparent);
}

.calendar tr.row-exam .c-topic {
  font-weight: 600;
  color: var(--strong);
}

.calendar tr.row-break td {
  color: var(--faint);
}

.calendar tr.row-break .c-topic {
  font-style: italic;
  color: var(--faint);
}

/* Hover last, so it wins over both the week band and the exam tint.
   Targets td because those rules do too. */
.calendar tbody tr:hover td {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

/* --- resource links inside cells --------------------------------- */

.res {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* Stacked variant: one chip per line. Used for the deliverable and lab
   cells, so a companion chip (starter kit) adds height to its row rather
   than width to the column. */
.res--stack {
  flex-direction: column;
  align-items: flex-start;
}

/* A downloadable PDF. */
.pdf {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.4;
  padding: 3px 7px;
  border: 1px solid var(--tag-border);
  color: var(--accent);
  white-space: nowrap;
}

.pdf:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: #ffffff;
}

.pdf::before {
  content: "↓";
  font-size: 10px;
  opacity: 0.75;
}

/* Secondary variant — finger exercises sit beside their deck. */
.pdf--alt {
  color: var(--muted);
  border-color: var(--ghost-border);
}

.pdf--alt:hover {
  background: var(--muted);
  border-color: var(--muted);
  color: var(--bg);
}

/* A deliverable with no file yet — deliberately not a link. */
.pending {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.4;
  padding: 3px 7px;
  border: 1px dashed var(--ghost-border);
  color: var(--faint);
  white-space: nowrap;
}

.pending::before {
  content: "○";
  font-size: 9px;
}

/* --- readings disclosure ------------------------------------------ */

/* A <details> whose <summary> is styled as a secondary chip. Closed, it
   sits in the chip row like any other chip. Open, it takes the row's full
   width, so the list lays out beneath the chips instead of being squeezed
   in beside them. No script involved. .chip-readings is the legend's
   inert copy of the same chip. */
.readings {
  display: block;
  min-width: 0;
}

.readings[open] {
  flex-basis: 100%;
}

.readings > summary,
.chip-readings {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.4;
  padding: 3px 7px;
  border: 1px solid var(--ghost-border);
  color: var(--muted);
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.readings > summary::-webkit-details-marker {
  display: none;
}

.readings > summary::before,
.chip-readings::before {
  content: "▸";
  font-size: 9px;
  opacity: 0.75;
}

.readings[open] > summary::before {
  content: "▾";
}

.readings > summary:hover {
  background: var(--muted);
  border-color: var(--muted);
  color: var(--bg);
}

/* Two columns: what to read, then why / how. The reference column grows
   to fit short refs ("Shirley §8.1.1") but is capped, so a long citation
   wraps instead of squeezing the note into a one-word-per-line sliver. */
.readings__list {
  display: grid;
  grid-template-columns: fit-content(13rem) minmax(10rem, 1fr);
  gap: 5px 14px;
  margin: 8px 0 2px 2px;
  font-size: 13px;
  line-height: 1.5;
}

.readings__list dt {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--strong);
  overflow-wrap: anywhere;
}

.readings__list dt a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid var(--tag-border);
}

.readings__list dt a:hover {
  border-bottom-color: var(--accent);
}

.readings__list dd {
  margin: 0;
  color: var(--muted);
}

.readings__list dd:empty {
  display: none;
}

@media (max-width: 480px) {
  .readings__list {
    grid-template-columns: 1fr;
    gap: 2px 0;
  }

  .readings__list dd {
    margin-bottom: 6px;
  }
}

/* Empty cell marker. */
.none {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--faint);
}

/* Legend above the table. */
.legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 20px;
  margin-bottom: 14px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--faint);
}

.legend__item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

/* On phones the table stops being a table. A 660px-wide grid inside a
   scroller means most columns sit off-screen, and the only affordance is a
   swipe the reader has to guess at — so each row becomes a stacked card
   with labelled fields instead. Nothing is hidden, nothing scrolls sideways.

   Labels come from data-label on each <td>, set in the generator. */
@media (max-width: 700px) {
  .calendar-scroll {
    overflow-x: visible;
    border: 0;
    background: transparent;
  }

  .calendar,
  .calendar tbody,
  .calendar tr,
  .calendar td {
    display: block;
    width: auto;
    min-width: 0;
  }

  /* The header row is meaningless once cells carry their own labels. */
  .calendar thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .calendar tr {
    border: 1px solid var(--panel-line);
    background: var(--panel-bg);
    margin-bottom: 10px;
    padding: 4px 0;
  }

  /* Week banding still applies, now per card. */
  .calendar tr.wk-odd {
    background: color-mix(in srgb, var(--wk-band) 100%, var(--panel-bg));
  }

  .calendar tr.wk-odd td,
  .calendar tbody tr:hover td {
    background: transparent;
  }

  .calendar tr.row-exam {
    background: color-mix(in srgb, var(--accent) 9%, var(--panel-bg));
  }

  .calendar tr.wk-start td {
    border-top: 0;
  }

  .calendar td {
    border-bottom: 0;
    padding: 5px 14px;
  }

  /* Label each field, so a cell is readable out of table context. */
  .calendar td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 3px;
    font-family: var(--mono);
    font-size: 10px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--faint);
  }

  /* Lec + date read as one header line at the top of each card. */
  .calendar .c-lec,
  .calendar .c-date {
    display: inline-block;
    padding-block: 2px;
  }

  .calendar .c-lec::before,
  .calendar .c-date::before {
    display: none;
  }

  .calendar .c-lec {
    padding-right: 4px;
    color: var(--accent);
    font-weight: 600;
  }

  /* Prefix the lecture number so it reads as "Lec 3 · Tue, 8 Sep". */
  .calendar .c-lec:not(:empty)::after {
    content: " ·";
    color: var(--faint);
    font-weight: 400;
  }

  .calendar .c-topic {
    padding-top: 2px;
    font-size: 15px;
  }

  /* Drop the em-dash placeholders — an absent field just isn't shown. */
  .calendar td:has(> .none) {
    display: none;
  }
}

/* --- student submissions (CS 453 calendar) ---------------------- */

/* "A<n> student submissions", in the calendar's Submissions column. It is
   emitted hidden and js/gallery.js shows it, since without the script it
   could not open anything. */
/* A <button>, so it drops the browser's button face to look like the
   other chips. */
.pdf--gallery {
  margin: 0;
  background: transparent;
  cursor: pointer;
}

.pdf--gallery::before {
  content: "▦";
}

.pdf--gallery[hidden] {
  display: none;
}

/* The popup: a native <dialog> with the assignment's thumbnails, each a
   square cropped from the centre of the piece (build_gallery.py), with the
   student's name on its bottom-left corner. The mat stays dark in both
   themes, as most of the scenes are night skies. */
.gallery {
  width: min(960px, calc(100vw - 32px));
  max-width: none;
  max-height: calc(100vh - 48px);
  max-height: calc(100dvh - 48px);
  padding: 0;
  border: 1px solid var(--panel-line);
  background: var(--panel-bg);
  color: var(--text);
  overflow: auto;
}

.gallery::backdrop {
  background: rgba(6, 7, 9, 0.72);
}

.gallery__bar {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 10px 10px 20px;
  background: var(--panel-bg);
  border-bottom: 1px solid var(--line);
}

.gallery__title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
}

.gallery__sub {
  margin-left: 10px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--faint);
}

.gallery__close {
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0 0 3px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
}

.gallery__close:hover {
  color: var(--accent);
  border-color: var(--ghost-border);
}

.gallery .tiles {
  list-style: none;
  margin: 0;
  padding: 16px 20px 20px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}

.gallery .tile {
  position: relative;
  display: block;
  aspect-ratio: 1;
  background: #08090b;
  border: 1px solid var(--panel-line);
  overflow: hidden;
}

.gallery .tile:hover,
.gallery .tile:focus-visible {
  border-color: var(--accent);
}

.gallery .tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.gallery .tile__name,
.lightbox__name,
.gallery .tile__badge {
  position: absolute;
  padding: 2px 6px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.4;
  color: #f4f2ee;
  background: rgba(0, 0, 0, 0.62);
  white-space: nowrap;
  pointer-events: none;
}

.gallery .tile__name,
.lightbox__name {
  left: 6px;
  bottom: 6px;
  max-width: calc(100% - 12px);
  overflow: hidden;
  text-overflow: ellipsis;
}

.gallery .tile__badge {
  top: 6px;
  right: 6px;
  font-size: 10px;
}

@media (max-width: 480px) {
  .gallery__sub {
    display: block;
    margin: 2px 0 0;
  }

  .gallery .tiles {
    grid-template-columns: repeat(2, 1fr);
    padding: 12px;
    gap: 8px;
  }
}

html:has(.gallery[open]),
html:has(.lightbox[open]) {
  overflow: hidden;
}

/* The full-screen view a thumbnail enlarges into: a second <dialog> over
   the popup, filling the viewport; Esc or the × goes back to the
   thumbnails. js/gallery.js fits the piece to the window keeping its
   shape, and adds .is-enlarged when that means more device pixels than the
   file has, so the browser keeps the pixels square instead of blurring. */
.lightbox {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: #d7d3cb;
  overflow: hidden;
}

.lightbox[open] {
  display: grid;
  grid-template: 52px 1fr 52px / 64px 1fr 64px;
  place-items: center;
}

/* Focused on open so Space and the arrows reach it; it is the whole
   viewport, so a focus ring would only be an odd frame. */
.lightbox:focus {
  outline: none;
}

.lightbox::backdrop {
  background: rgba(6, 7, 9, 0.96);
}

.lightbox__bar {
  grid-area: 1 / 1 / 2 / 4;
  justify-self: stretch;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px 0 20px;
  font-family: var(--mono);
  font-size: 12px;
  color: #a8a49b;
}

.lightbox__figure {
  grid-area: 2 / 2;
  position: relative;
  margin: 0;
  line-height: 0;
}

.lightbox__media {
  display: block;
  background: #000;
}

.lightbox__media.is-enlarged {
  image-rendering: pixelated;
}

.lightbox__name {
  left: 8px;
  bottom: 8px;
  font-size: 12px;
}

video.lightbox__media {
  cursor: pointer;
}

/* Shown only while a video is paused: after a beat, so autoplay starting
   does not flash it. */
.lightbox__play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 64px;
  height: 64px;
  display: grid;
  place-items: center;
  font-size: 22px;
  line-height: 1;
  color: #ffffff;
  background: rgba(0, 0, 0, 0.55);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}

.lightbox__figure.is-paused .lightbox__play {
  opacity: 1;
  transition: opacity 0.15s 0.3s;
}

.lightbox__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0 0 3px;
  border: 1px solid transparent;
  background: transparent;
  color: #d7d3cb;
  font-family: var(--mono);
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
}

.lightbox__btn:hover {
  color: var(--accent);
  border-color: #3a3e45;
}

.lightbox__prev {
  grid-area: 2 / 1;
}

.lightbox__next {
  grid-area: 2 / 3;
}

/* Phones: the picture takes the full width; the arrows float over its
   edges (a swipe works too). */
@media (max-width: 640px) {
  .lightbox[open] {
    grid-template-columns: 8px 1fr 8px;
  }

  .lightbox__prev,
  .lightbox__next {
    position: fixed;
    top: 50%;
    z-index: 1;
    translate: 0 -50%;
    background: rgba(0, 0, 0, 0.45);
  }

  .lightbox__prev {
    left: 6px;
  }

  .lightbox__next {
    right: 6px;
  }
}

/* --- footer ----------------------------------------------------- */

.footer {
  border-top: 1px solid var(--line);
}

.footer__inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 48px 24px 40px;
  display: grid;
  gap: 32px;
}

.footer__bio {
  margin: 0;
  font-size: 16px;
  line-height: 1.65;
  color: var(--muted);
  max-width: 62ch;
  text-wrap: pretty;
}

.footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
}

.footer__social {
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
  font-family: var(--mono);
  font-size: 13px;
}

.footer__social a {
  color: var(--muted);
}

.footer__social a:hover {
  color: var(--accent);
}

.footer__colophon {
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--faint);
}

/* --- small screens ---------------------------------------------- */

@media (max-width: 640px) {
  .nav {
    flex-wrap: wrap;
    gap: 14px;
  }

  .nav__links {
    gap: 10px 18px;
    font-size: 13px;
    order: 3;
    width: 100%;
  }

  /* Let the hero be as tall as its content needs and no taller. A fixed
     vh height left a large dead band between the nav and the headline,
     because the text is bottom-aligned and the render's subject sits low. */
  .hero {
    min-height: 62vh;
  }

  .hero__inner {
    padding: 40px 24px 36px;
  }

  /* The tall render's top ~30% is empty near-black sky; its subject sits
     around 50-70% down. At phone aspect ratios `cover` fits the image by
     width, so there is no vertical overflow for object-position to shift —
     the dark band is forced into frame.

     Scaling to 145% height makes the image overflow vertically, which gives
     object-position something to work with, and 62% centres the crop on the
     spheres instead of the sky. */
  .hero__media img {
    height: 145%;
    /* Pull the frame down so the dark upper third sits above the clip. */
    position: absolute;
    top: -32%;
    left: 0;
  }

  .bench__label {
    min-width: 0;
    flex: 1 1 100%;
  }

  .post__date,
  .entry__side {
    flex-basis: 100%;
  }
}
