/* ============================================================================
   nōesis · noesis.css
   Tokens, type scale, chapter layouts.
   Motion lives in motion.css.
   ========================================================================== */

/* ---- 1. tokens ---------------------------------------------------------- */
:root{
  /* brand hexes, straight from the brand book. Do not invent new ones. */
  --charcoal:#2B2B2B;
  --stone:#D4D0C8;
  --taupe:#B0A9A0;
  --grey:#808080;
  --rose:#89525A;
  --offwhite:#F8F8F8;

  /* semantic. charcoal is the DEFAULT ground; stone is an interruption. */
  --ground:var(--charcoal);
  --ink:var(--stone);
  /* Stone reads the sentences, taupe holds the chrome back. The dim tier
     used to carry both, and paragraphs set in it went thin on a charcoal
     ground. It is now reserved for things meant to recede: rail labels,
     the top bar, the footer, form captions. */
  --ink-dim:var(--taupe);
  --ink-mute:#9C968E;
  --accent:var(--rose);
  /* Rose and grey are perfect as drawn marks and hopeless as small text:
     on charcoal the rose runs 2.34:1 and the grey 3.64:1, both under the
     4.5:1 that 12px type needs. These two are the same colours lifted
     toward stone for TEXT only, at 4.99:1 and 4.60:1. Every rule, dot,
     bead and drawn mark still takes --accent and --grey at full strength,
     which is why the palette above is untouched. */
  --accent-ink:#B2919A;
  --rule:rgba(212,208,200,.20);
  --rule-strong:rgba(212,208,200,.42);
  --plate-bg:#232323;

  /* typefaces. --font-display swaps to Nondas's pick from specimen.html. */
  --font-display:'Literata',Georgia,serif;
  --font-text:'Manrope',system-ui,-apple-system,sans-serif;
  /* Quicksand is the brand font for the tagline, straight off the brand sheet.
     It has NO Greek subset, which is why it cannot carry the body copy.

     It has exactly TWO sanctioned uses, and both pass the same test: the
     string is Latin and identical in both language versions, so the missing
     Greek can never be reached. Those are "Elevated Wellness" in the lockup
     and the tagline, and the name Range+ wherever it is set as a logotype
     (see .rangemark below). Do not reuse it anywhere a Greek word could land. */
  --font-tagline:'Quicksand',var(--font-text);

  /* Type scale. The two ends were pulled in: --t-micro carries 28 labels
     across the site, so it sets the floor for everything a visitor has to
     READ, and the hero was reaching 152px on a wide screen, which left the
     chapter titles looking like a second thought. */
  --t-hero:clamp(2.9rem,10vw,8.25rem);
  --t-chapter:clamp(2rem,6vw,4.75rem);
  --t-quote:clamp(1.4rem,3vw,2.35rem);
  --t-title:clamp(1.25rem,2.4vw,1.9rem);
  --t-lead:clamp(1.02rem,1.5vw,1.3rem);
  --t-body:1rem;
  --t-small:.9375rem;
  --t-micro:.8rem;

  /* spacing */
  --s1:.5rem; --s2:1rem; --s3:1.5rem; --s4:2.5rem;
  --s5:4rem;  --s6:6rem; --s7:9rem;   --s8:13rem;

  /* the left gutter the trail rail lives in, and the margin column
     where captions, indices, bios and countdowns print */
  --rail:212px;
  --margin-col:clamp(0px,13vw,190px);
  --pad-x:clamp(1.25rem,4vw,3.5rem);
  --measure:66ch;
  /* The widest ink any ruled row carries. The rules used to run the full width
     of the page while the text inside them stopped two thirds of the way
     across, so every row ended in a long stretch of empty hairline that read
     as content failing to arrive. The rules now stop where the reading does.
     One number, because the tenets and the Κώδικας have to agree: a page with
     two different rule lengths on it reads as a mistake rather than a system.
     The ledger is deliberately NOT bound by this; a timetable is a table and
     wants its full width. */
  --ruled-max:37rem;
}

/* ---- 2. reset ----------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}
*{margin:0;padding:0}
/* Deliberately NO overflow property on html or body.
   `overflow-x:hidden` on body forces overflow-y to compute to `auto`, which
   makes body a scroll container and stops scroll events reaching the window,
   killing every scroll-driven effect here. `overflow-x:clip` on html stops the
   root scrolling altogether in Chrome. Nothing on this page overflows
   horizontally at any width, and the one element that does, the team rail, is
   contained by .rail-wrap. So neither is needed. Do not add one back without
   re-testing the trail rail and the wandering line. */
html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  background:var(--ground);
  color:var(--ink);
  font-family:var(--font-text);
  font-size:var(--t-body);
  font-weight:300;
  line-height:1.65;
  transition:background .6s ease,color .6s ease;
}
img,svg{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
a{color:inherit;text-decoration:none}

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

/* ---- 3. paper grain + vignette ------------------------------------------ */
.grain{
  position:fixed; inset:0; z-index:60; pointer-events:none;
  opacity:.055; mix-blend-mode:overlay;
}
.vignette{
  position:fixed; inset:0; z-index:59; pointer-events:none;
  background:radial-gradient(ellipse at 50% 45%,transparent 52%,rgba(0,0,0,.34) 100%);
}

/* ---- 4. shared type ----------------------------------------------------- */
.display{
  font-family:var(--font-display);
  font-weight:200;
  line-height:1.03;
  letter-spacing:-.018em;
}
.micro{
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-mute);
  font-weight:500;
}
/* Range+ is a sub-brand and it gets the brand's OWN display face, Quicksand,
   the one the lockup sets "Elevated Wellness" in. Setting the whole name in
   it, rather than grafting a sans plus onto a serif word, makes the name a
   logotype wherever it appears, and disposes of the plus problem on the way:
   Literata draws a small high plus that reads as a dagger at the pager's 28px
   and as an outright cross at the mobile menu's 38px.

   Weight is inherited on purpose. Quicksand is loaded at 300..600, so inside a
   200-weight Literata heading it renders at its lightest, and inside the rail's
   500 it comes out at 500, without this rule knowing anything about either. */
.rangemark{
  font-family:var(--font-tagline);
  font-weight:400;
  font-size:.96em;
  letter-spacing:-.01em;
}

/* A plus that is NOT part of the name: the 5+ and 30+ figures on the Range+
   page are numerals in the display face, so they still take a text-face plus
   rather than a logotype. */
.plus{
  font-family:var(--font-text);
  font-weight:300;
  font-size:.84em;
  letter-spacing:0;
  /* Manrope carries more side bearing than Literata, which left the plus
     floating a word's width away from the digit. In em, so it holds at every
     size the figures are set at. */
  margin-left:-.07em;
}

.lead{
  font-size:var(--t-lead);
  /* 1.6 is a body-copy figure. At 20.8px in a 300 weight on a dark ground the
     lines were closing up on each other, so the lead gets its own leading. */
  line-height:1.72;
  color:var(--ink);
  max-width:52ch;
  font-weight:300;
}
/* ---- 5. page shell ------------------------------------------------------ */
.shell{padding-left:var(--rail)}
@media (max-width:1280px){ :root{--rail:190px} }
/* below this the bar takes over from the rail (see motion.css) */
@media (max-width:1080px){
  :root{--rail:0px;--margin-col:0px}
  .shell{padding-left:0;padding-top:66px}
}

.chapter{
  position:relative;
  /* Asymmetric on purpose. It used to be --s8 both ends, which put 208px under
     the last row of a section and another 208px over the next heading: about
     400px of nothing between two blocks. A heading wants air above it to
     announce a new chapter; a list that has finished does not need the same
     again below. So the top keeps its full measure and the bottom takes two
     thirds of it. */
  padding:var(--s8) var(--pad-x) var(--s6);
  border-top:1px solid var(--rule);
  /* Skip the layout and paint of chapters that are nowhere near the viewport.
     Range+ is nearly 8000px tall and every one of its sections was being laid
     out and painted on load whether or not anybody could see it.

     `auto` on contain-intrinsic-size is the part that makes this safe here. A
     fixed placeholder height would report a wrong document height while the
     page is off screen, and on this site the spine is drawn against the
     document height and lands on the footer's rule: a wrong height and the
     route stops in mid-air. `auto` makes the browser remember each section's
     real size once it has been rendered and keep using it, so the measurement
     the route depends on stays true. 900px is only the first guess, before
     anything has been seen. */
  content-visibility:auto;
  contain-intrinsic-size:auto 900px;
}
.chapter:first-of-type{border-top:0}

/* the chapter head: numeral, Greek title, English gloss. The numeral is not
   decoration, it is the reader's position on the path. */
.chapter-head{
  display:grid;
  grid-template-columns:var(--margin-col) 1fr;
  gap:0 var(--s4);
  margin-bottom:var(--s6);
  align-items:start;
}
.chapter-head .mark{
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--accent-ink);
  padding-top:.85em;
  white-space:nowrap;
}
.chapter-head h2{
  font-family:var(--font-display);
  font-weight:200;
  font-size:var(--t-chapter);
  line-height:1.02;
  letter-spacing:-.02em;
}
.chapter-head .gloss{
  display:block;
  font-family:var(--font-text);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-mute);
  font-weight:500;
  margin-top:var(--s2);
}
/* The lead shares the right edge of the rules below it. On its own measure it
   overhung them by about 20px, which is too little to read as a deliberate
   step out and too much to look level: the block just looked crooked. */
.chapter-head .lead{margin-top:var(--s3);max-width:var(--ruled-max)}
@media (max-width:860px){
  .chapter{padding:var(--s6) var(--pad-x) var(--s5)}
  .chapter-head{grid-template-columns:1fr;gap:var(--s2)}
  .chapter-head .mark{padding-top:0}
}

/* ---- 6. I · Ο ΧΩΡΟΣ ΠΟΥ ΟΝΕΙΡΕΥΤΗΚΑΜΕ · hero ---------------------------- */
.hero{
  min-height:100svh;
  display:flex;
  flex-direction:column;
  justify-content:center;
  padding:var(--s7) var(--pad-x) var(--s6);
  position:relative;
}
.hero .wordmark{
  width:clamp(150px,17vw,232px);
  margin-bottom:var(--s5);
}
.hero h1{
  font-family:var(--font-display);
  font-weight:200;
  font-size:var(--t-hero);
  line-height:.98;
  letter-spacing:-.03em;
  max-width:14ch;
}
.hero .sub{
  margin-top:var(--s4);
  font-size:var(--t-lead);
  color:var(--ink);
  max-width:38ch;
  font-weight:300;
}
.hero .begin{
  margin-top:var(--s5);
  display:inline-flex;
  align-items:center;
  gap:var(--s2);
  align-self:flex-start;
  padding:.95rem 1.9rem;
  border:1px solid var(--rule-strong);
  border-radius:0;
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  font-weight:500;
  color:var(--ink);
  transition:background .35s ease,color .35s ease,border-color .35s ease;
}
/* The hover lives in motion.css now, with the rail's: outline extrudes, the
   rosewood bar fills along the bottom edge. The solid stone flood that used to
   be here made this the one control on the site that inverted itself. */
.hero .begin .arrow{transition:transform .35s ease}
.hero .begin:hover .arrow{transform:translateX(5px)}

/* ---- 7. philosophy: ruled column of large quote type -------------------- */
.tenets{
  border-top:1px solid var(--rule);
  max-width:calc(var(--margin-col) + var(--s4) + var(--ruled-max));
}
.tenet{
  display:grid;
  grid-template-columns:var(--margin-col) 1fr;
  gap:0 var(--s4);
  padding:var(--s5) 0;
  border-bottom:1px solid var(--rule);
  align-items:baseline;
}
.tenet .n{
  font-family:var(--font-display);
  font-size:var(--t-title);
  color:var(--accent);
  font-weight:300;
  font-variant-numeric:tabular-nums;
}
.tenet p{
  font-family:var(--font-display);
  font-weight:200;
  font-size:var(--t-quote);
  line-height:1.22;
  letter-spacing:-.012em;
  max-width:24ch;
}
@media (max-width:860px){
  .tenet{grid-template-columns:1fr;gap:var(--s2);padding:var(--s4) 0}
}

/* The column now carries the founders' paragraphs rather than one-line
   tenets. Left at quote size on a 24ch measure a 290-character paragraph runs
   twelve lines of 38px display type: a wall, and the numeral loses the line it
   is meant to sit beside. So the prose rows keep the face, the weight and the
   rules, and give up only the size and the narrow measure. */
.tenets--prose .tenet p{
  font-size:clamp(1.2rem,2vw,1.75rem);
  line-height:1.36;
  letter-spacing:-.008em;
  max-width:33ch;
}
/* At quote size the numeral aligned on the baseline of a single big line.
   Against a paragraph it needs its own optical nudge to sit with the first
   line rather than below it. */
.tenets--prose .tenet .n{
  font-size:clamp(1.15rem,2vw,1.6rem);
  line-height:1.36;
}

/* ---- 8. gallery: the constellation -------------------------------------- */
.constellation{
  position:relative;
  margin-top:var(--s7);
  min-height:min(190vh,1500px);
}
/* Every plate carries the same hairline, whether or not a photograph has
   landed in it yet, so a part-filled gallery still reads as one set. */
.plate{
  position:absolute;
  overflow:hidden;
  background:var(--plate-bg);
  cursor:pointer;
  border:1px solid var(--rule);
  padding:0;
  display:block;
  transition:border-color .35s ease;
}
.plate:hover{border-color:var(--rule-strong)}
.plate img{width:100%;height:100%;object-fit:cover}
/* photos arrive in colour and are duotoned here, so a mismatched set
   still reads as one body of work. */
.duo{filter:url(#noesis-duotone)}
@supports not (filter:url(#noesis-duotone)){
  .duo{filter:grayscale(1) contrast(1.08) brightness(.92)}
}
.plate .empty{
  position:absolute;inset:0;
  display:grid;place-items:center;
  color:var(--ink-mute);
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  text-align:center;
  padding:var(--s2);
}
/* caption prints in the MARGIN, never over the image */
.plate-caption{
  position:absolute;
  left:var(--pad-x);
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--accent-ink);
  pointer-events:none;
  max-width:var(--margin-col);
  line-height:1.5;
}
@media (max-width:860px){
  .constellation{
    position:static;min-height:0;
    display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s2);
  }
  /* --plate-ar is set per plate in gallery.js, so a landscape frame stays
     landscape here instead of being cropped to the portrait default */
  .plate{
    position:static!important;width:auto!important;height:auto!important;
    aspect-ratio:var(--plate-ar,3/4);
  }
  .plate-caption{display:none}
}

/* ---- 9. II · ΟΙ ΣΥΝΟΔΟΙΠΟΡΟΙ · rail that breaks the container ----------- */
/* Bleeds off the RIGHT edge only. Bleeding left as well pushed the first
   portrait flush against the menu rail, so it read as stuck to it. The first
   card now starts on the same line as the text above it. */
.rail-wrap{
  margin-right:calc(var(--pad-x) * -1);
  overflow:hidden;
}
.rail{
  display:flex;
  gap:var(--s3);
  padding:0 var(--pad-x) 0 0;
  overflow-x:auto;
  scroll-snap-type:x proximity;
  scrollbar-width:none;
  cursor:grab;
}
.rail::-webkit-scrollbar{display:none}
.rail.dragging{cursor:grabbing;scroll-snap-type:none}
.guide{
  flex:0 0 clamp(230px,26vw,330px);
  scroll-snap-align:start;
}
.guide .portrait{
  aspect-ratio:3/4.3;
  background:var(--plate-bg);
  position:relative;
  overflow:hidden;
  margin-bottom:var(--s3);
}
.guide .portrait img{width:100%;height:100%;object-fit:cover}
.guide .sigil{
  position:absolute;
  right:var(--s2); bottom:var(--s2);
  width:38px; height:38px;
  opacity:.85;
}
/* Two lines of height whether the name needs them or not. "Κωνσταντίνος
   Μαρκόζης" wraps and the other two names do not, so their disciplines and
   bios started a line apart and the row of cards read as misaligned rather
   than as a row. Reserving the second line costs a little air under the short
   names and buys every card the same three horizontals. Widen it to 3lh if a
   teacher ever arrives with a name long enough to take a third line. */
.guide h3{
  font-family:var(--font-display);
  font-weight:300;
  font-size:var(--t-title);
  line-height:1.15;
  letter-spacing:-.01em;
  min-height:calc(var(--t-title) * 1.15 * 2);
}
.guide .disc{
  margin-top:var(--s1);
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--accent-ink);
  font-weight:500;
}
.guide .bio{
  margin-top:var(--s2);
  font-size:var(--t-small);
  line-height:1.6;
  color:var(--ink);
  max-width:34ch;
}
.rail-hint{
  margin-top:var(--s3);
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--ink-mute);
}

/* ---- 10a. III · what is taught, above the timetable ---------------------
   The same margin-column rhythm as the tenets and the Code, so the four
   practices sit on the page's existing grid, but with no icons and no
   numerals: the Κώδικας further down is already an icon list, and running two
   of them on one page makes the second read as decoration. */
.classes{border-top:1px solid var(--rule);margin-top:var(--s5)}
.class-entry{
  display:grid;
  grid-template-columns:var(--margin-col) 1fr;
  gap:var(--s2) var(--s4);
  padding:var(--s4) 0;
  border-bottom:1px solid var(--rule);
  align-items:start;
}
.class-entry h3{
  font-family:var(--font-display);
  font-weight:300;
  font-size:var(--t-title);
  line-height:1.2;
  padding-top:.1em;
}
/* the age marker rides under the name rather than beside it, so a long name
   and a short one still line up on the same left edge */
.class-age{
  display:block;
  margin-top:.35em;
  font-family:var(--font-text);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  color:var(--accent-ink);
  font-weight:500;
}
.class-entry p{
  font-size:var(--t-body);
  line-height:1.75;
  color:var(--ink);
  max-width:62ch;
}
@media (max-width:860px){
  .class-entry{grid-template-columns:1fr;gap:var(--s2);padding:var(--s3) 0}
  .class-age{display:inline-block;margin:0 0 0 var(--s2)}
}

/* ---- 10. III · ΔΙΑΔΡΟΜΕΣ · the ledger ----------------------------------- */
.days{
  display:flex;
  flex-wrap:wrap;
  gap:var(--s4);
  align-items:baseline;
  border-bottom:1px solid var(--rule-strong);
  padding-bottom:var(--s2);
  margin-bottom:var(--s4);
}
/* seven Greek day abbreviations at a 2.5rem gap wrap onto three lines on a
   phone. Tighten the gap and let them spread instead. */
@media (max-width:560px){
  .days{gap:0;justify-content:space-between}
  .day-btn{letter-spacing:.12em}
}
.day-btn{
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-mute);
  font-weight:500;
  padding:.35rem 0;
  border-bottom:1px solid transparent;
  transition:color .3s ease,border-color .3s ease;
}
.day-btn:hover{color:var(--ink)}
.day-btn[aria-selected="true"]{color:var(--ink);border-bottom-color:var(--accent)}

.ledger{border-top:1px solid var(--rule)}
.row{border-bottom:1px solid var(--rule)}
/* The name column is capped rather than greedy. As a bare 1fr it grew to the
   full width of the ledger, and since a class name is one or two words the row
   ran about two thirds empty between the name and the level marks: a canyon,
   not a ledger. Capped at 22rem the left of the row reads as one group, time,
   name and duration together, while the slack collects in the fourth track so
   the marks and the plus still anchor hard right where the eye expects to find
   them. The columns stay fixed widths, so they line up down the whole table;
   clustering each row to its own content would have made them ragged. */
.row-head{
  width:100%;
  display:grid;
  grid-template-columns:5.5rem minmax(0,22rem) 9rem 1fr 14px;
  gap:var(--s3);
  align-items:baseline;
  padding:var(--s3) 0;
  text-align:left;
  transition:color .3s ease;
}
/* the marks live at the far end of the slack column, beside the plus */
.row-head .lvl{justify-self:end}
.row-head:hover{color:var(--ink)}

/* A row opens, and nothing on it said so. The rule was reachable by keyboard
   and correct to a screen reader the whole time, but a sighted visitor had to
   guess, and the hover tint only appears once you are already on it, which is
   too late to be an invitation. So the affordance is drawn, in the same single
   hairline the rule marks and the teacher sigils are drawn in, and it is
   visible at rest: the same argument the rail makes about labels. */
/* Level with the FIRST line of the class name, not with the centre of the
   whole row: the teacher and the level sit under the name on a narrow column,
   and a mark centred on all three floats away from the thing it belongs to.
   Derived from --t-title so it holds as the name scales. */
.open-mark{
  position:relative;
  align-self:start;
  justify-self:end;
  margin-top:calc((var(--t-title) * 1.2 - 14px) / 2);
  width:14px; height:14px;
  color:var(--accent-ink);
  transition:color .3s ease;
}
.open-mark::before,
.open-mark::after{
  content:'';
  position:absolute;
  top:50%; left:0;
  width:14px; height:1px;
  background:currentColor;
}
/* the upright stroke of the plus. It lies down as the row opens, so the mark
   ends up a minus without anything appearing or disappearing */
.open-mark::after{
  transform:rotate(90deg);
  transition:transform .45s cubic-bezier(.22,1,.36,1);
}
.row[data-open="true"] .open-mark::after{transform:rotate(0deg)}
.row-head:hover .open-mark{color:var(--ink)}
.row-head .t{
  font-variant-numeric:tabular-nums;
  font-size:var(--t-small);
  letter-spacing:.08em;
  color:var(--accent-ink);
}
.row-head .nm{
  font-family:var(--font-display);
  font-weight:300;
  font-size:var(--t-title);
  line-height:1.2;
}
.row-head .tc{
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  /* taupe, not the mute grey the other labels take. This is the smallest text
     on the page and it now sits over a photograph, which costs about half a
     point of contrast: on grey it landed at 4.3:1, under the 4.5 that 12.8px
     type needs, and on taupe it clears at 5.4:1. */
  color:var(--ink-dim);
}
/* the duration standing in for the teacher. Same slot, same size and colour,
   but lining figures so the minutes stack into a column down the table the way
   the times on the left already do. */
.row-head .tc--dur{
  font-variant-numeric:tabular-nums;
  letter-spacing:.1em;
}
/* level drawn as ascending marks on a path segment, not a pill */
/* width:max-content so the box hugs its three marks instead of stretching
   across the whole 5rem column. Nothing moves, the marks were already at the
   left of it, but it gives the all-levels rule below something the right
   length to span. */
/* 20px across, up from 15. They were sized when they were one of five things
   on a row and the least of them; they now carry the whole of what a class
   asks of you, and at 15px with a 3px stroke that reading was a squint. */
.lvl{
  display:flex;
  align-items:flex-end;
  gap:4px;
  height:20px;
  width:max-content;
  position:relative;
}
.lvl i{width:4px;background:var(--rule-strong);display:block}
.lvl i:nth-child(1){height:8px}
.lvl i:nth-child(2){height:14px}
.lvl i:nth-child(3){height:20px}
.lvl i.on{background:var(--accent)}
/* All levels. Lighting all three marks said Advanced, and on a timetable where
   most classes are open to everyone that ranked the easy ones hardest. It is
   not the top of the scale, it is the scale not applying, so the marks stay
   unlit and a rule runs under all three: the same accent, saying "the whole
   span" rather than "the far end of it". The words are still on the row's
   title and in the open body, which is what a screen reader reads. */
.lvl--all::after{
  content:'';
  position:absolute;
  left:0; right:0; bottom:-5px;
  height:1px;
  background:var(--accent);
}
.row-body{
  display:grid;
  grid-template-rows:0fr;
  transition:grid-template-rows .45s cubic-bezier(.4,0,.2,1);
}
.row[data-open="true"] .row-body{grid-template-rows:1fr}
.row-body>div{overflow:hidden}
.row-body .inner{
  padding:0 0 var(--s3) 5.5rem;
  max-width:var(--measure);
}
.row-body p{color:var(--ink);font-size:var(--t-small);line-height:1.7}
/* the duration and the level, under the description. This carried its own
   font-size two hundred lines further down, where `.row-body p` outranked
   it and the size silently never applied. */
.row-body .row-meta{
  margin-top:.5rem;
  color:var(--ink-mute);
  font-size:var(--t-micro);
  letter-spacing:.12em;
  text-transform:uppercase;
}
.row-body .book{
  display:inline-block;
  margin-top:var(--s2);
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--accent-ink);
  font-weight:500;
  border-bottom:1px solid var(--rule-strong);
  padding-bottom:.25rem;
  transition:border-color .3s ease;
}
.row-body .book:hover{border-bottom-color:var(--accent)}
@media (max-width:760px){
  .row-head{
    grid-template-columns:4.5rem 1fr 14px;
    grid-template-areas:'t nm mk' '. tc tc' '. lv lv';
  }
  /* On a phone the duration and the marks are both short, so they share one
     band under the name instead of taking a line each. */
  .row-head{grid-template-areas:'t nm mk' '. lv tc'}
  .row-head .t{grid-area:t}.row-head .nm{grid-area:nm}
  .row-head .tc{grid-area:tc;justify-self:end}
  .row-head .lvl{grid-area:lv;justify-self:start}
  .open-mark{grid-area:mk}
  .row-body .inner{padding-left:4.5rem}
}

/* ---- 11. Ο Κώδικας ------------------------------------------------------ */
/* The Code opens the second half of the Routes page, and it was titled with
   the same 12.8px kicker the gallery uses over a grid of plates. Right for a
   label, wrong for a heading with eight rules under it: the title was easy to
   scroll straight past. Display face now, and a clear step below the page
   title so the page still reads h1 76 > h2 50 > rule 27. */
.section-title{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.9rem,4vw,3.1rem);
  line-height:1.05;
  letter-spacing:-.02em;
  margin-bottom:var(--s3);
}
.code-list{
  border-top:1px solid var(--rule);
  margin-top:var(--s6);
  max-width:calc(var(--margin-col) + var(--s4) + var(--ruled-max));
}
.code-item{
  display:grid;
  grid-template-columns:var(--margin-col) 1fr;
  gap:0 var(--s4);
  padding:var(--s4) 0;
  border-bottom:1px solid var(--rule);
  align-items:start;
}
/* the drawn mark and the index share the margin column */
.code-mark{
  display:flex;
  align-items:center;
  gap:var(--s2);
  color:var(--accent);
  padding-top:.15em;
}
.rule-mark{width:32px;height:32px;flex:0 0 32px}
.code-item .n{
  font-family:var(--font-display);
  font-size:.86rem;
  color:var(--ink-mute);
  font-weight:300;
  font-variant-numeric:tabular-nums;
  letter-spacing:.06em;
}
/* the heading carries the display face; the rule itself is read at body size,
   because these are instructions and the visitor has to get through eight */
.code-body h3{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.15rem,2.2vw,1.7rem);
  line-height:1.2;
  letter-spacing:-.012em;
  margin-bottom:var(--s2);
}
.code-body p{
  font-size:var(--t-body);
  line-height:1.7;
  color:var(--ink);
  /* the measure is held where it was: at body size 62ch would have run the
     line out to nearly 600px, and eight of those is a wall */
  max-width:56ch;
}
.code-outro{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.2rem,2.6vw,2rem);
  line-height:1.3;
  letter-spacing:-.012em;
  color:var(--ink);
  max-width:34ch;
  padding-top:var(--s5);
}
@media (max-width:860px){
  .code-item{grid-template-columns:1fr;gap:var(--s2)}
  .code-mark{padding-top:0}
}

/* ---- 11b. IV · RANGE+ -------------------------------------------------
   The page is assembled almost entirely from patterns the site already owns:
   the claims are tenets, the inclusions are Code rows, the audiences are
   volunteer cards. Only three things here are new. */

/* Paragraphs in a row need air between them, or they read as one block that
   happens to have a short line in the middle. The opening paragraph of each
   block carries lead size and the rest sit at body size: this is the one page
   written in the first person, and three paragraphs at 20px is a wall. */
.prose{display:grid;gap:var(--s3)}
.prose p{
  font-size:var(--t-body);
  line-height:1.75;
  color:var(--ink);
  max-width:62ch;
}
/* `.prose p` outranks the bare `.lead` class, so the opening paragraph
   has to be told its own size here or it silently drops to body size */
.prose .lead{
  font-size:var(--t-lead);
  line-height:1.72;
  max-width:52ch;
}

/* the pillar's one-line instruction, between its name and its explanation */
.pillar-say{
  font-family:var(--font-display);
  font-weight:300;
  font-size:var(--t-lead);
  line-height:1.35;
  color:var(--accent-ink);
  margin-bottom:var(--s1);
}

/* the two figures under the practice section. Both come out of the copy. */
.figures{
  display:flex;
  flex-wrap:wrap;
  gap:var(--s5) var(--s7);
  margin-top:var(--s5);
  padding-top:var(--s4);
  border-top:1px solid var(--rule);
}
.figures dt{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(2.4rem,5vw,3.6rem);
  line-height:1;
  letter-spacing:-.02em;
  color:var(--accent-ink);
}
.figures dd{
  margin-top:var(--s2);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-mute);
  max-width:22ch;
}

/* the mark's own line, before the one action on the page */
.strapline{
  font-family:var(--font-display);
  font-weight:200;
  font-size:var(--t-quote);
  line-height:1.25;
  letter-spacing:-.012em;
  max-width:24ch;
}

/* the frames the photographs will occupy. Portrait, because the movements
   this page is about are taller than they are wide. */
.range-plates{
  display:grid;
  gap:var(--s4);
  grid-template-columns:repeat(auto-fit,minmax(min(100%,15rem),1fr));
  margin-top:var(--s6);
}
/* the sized box is the frame, exactly as on an expedition, so the shared
   placeholder in section 19 can fill it */
.range-plate .frame{
  position:relative;
  aspect-ratio:4/5;
  background:var(--plate-bg);
  overflow:hidden;
}
.range-plate img{width:100%;height:100%;object-fit:cover}
.range-plate figcaption{
  margin-top:var(--s2);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-mute);
}

/* the one action on the page, the same outlined control as the hero's */
.range-cta{
  margin-top:var(--s5);
  display:inline-flex;
  align-items:center;
  padding:.95rem 1.9rem;
  border:1px solid var(--rule-strong);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  font-weight:500;
  color:var(--ink);
  transition:background .35s ease,color .35s ease,border-color .35s ease;
}
/* hover in motion.css, shared with the rail and the other two CTAs */

/* ---- 12. IV · ΔΡΑΣΕΙΣ --------------------------------------------------- */
.group-label{
  display:flex;
  align-items:baseline;
  gap:var(--s3);
  margin:var(--s7) 0 var(--s4);
  font-size:var(--t-micro);
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--accent-ink);
  font-weight:500;
}
.group-label::after{
  content:'';flex:1;height:1px;background:var(--rule);
}

/* ahead: full-bleed lit plates */
.ahead{display:grid;gap:var(--s5)}
.expedition{
  display:grid;
  grid-template-columns:var(--margin-col) 1fr;
  gap:0 var(--s4);
  align-items:start;
}
.expedition .when{
  font-family:var(--font-display);
  font-weight:200;
  color:var(--accent-ink);
  line-height:1;
}
.expedition .when .d{color:var(--accent);font-size:clamp(2.2rem,4.5vw,3.4rem);display:block}
.expedition .when .m{
  font-family:var(--font-text);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  display:block;
  margin-top:var(--s1);
  color:var(--ink-mute);
}
.expedition .when .away{
  display:block;margin-top:var(--s2);
  font-family:var(--font-text);
  font-size:var(--t-micro);
  letter-spacing:.14em;
  color:var(--ink-mute);
  text-transform:none;
}
.expedition .plate-lg{
  aspect-ratio:16/7;
  background:var(--plate-bg);
  overflow:hidden;
  margin-bottom:var(--s3);
  position:relative;
}
.expedition .plate-lg img{width:100%;height:100%;object-fit:cover}
.expedition h3{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.6rem,3.4vw,2.6rem);
  line-height:1.1;
  letter-spacing:-.015em;
}
.expedition .place{
  margin-top:var(--s1);
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--ink-mute);
}
.expedition p{margin-top:var(--s2);color:var(--ink);max-width:52ch;font-size:var(--t-small);line-height:1.7}

/* behind: dimmed, receding, ink-stamp dates */
.behind{border-top:1px solid var(--rule)}
.past{
  display:grid;
  grid-template-columns:var(--margin-col) 1fr auto;
  gap:0 var(--s4);
  padding:var(--s3) 0;
  border-bottom:1px solid var(--rule);
  align-items:baseline;
  opacity:.58;
  transition:opacity .4s ease;
}
.past:hover{opacity:1}
.past .stamp{
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-mute);
  border:1px solid var(--rule);
  padding:.3rem .6rem;
  display:inline-block;
  transform:rotate(-1.5deg);
}
.past h3{
  font-family:var(--font-display);
  font-weight:300;
  font-size:var(--t-title);
  line-height:1.2;
}
.past p{font-size:var(--t-small);color:var(--ink);margin-top:var(--s1);max-width:48ch}
@media (max-width:860px){
  .expedition,.past{grid-template-columns:1fr;gap:var(--s2)}
  .past{grid-template-columns:1fr}
}

/* offerings: the stone interruption */
/* The volunteer block. It used to be a full-width stone panel, which on a
   charcoal site read as a big white band and broke the page in half. It stays
   on the charcoal ground now and is marked out by a rosewood rule and its own
   label instead, which is enough to say "a different kind of act". */
/* No accent tab on the top rule here. It sat 144px above its own label with
   nothing around it, so it read as a stray mark rather than as the start of a
   section, and the label underneath already says where the section begins. */
.offerings{
  padding:var(--s7) var(--pad-x);
  border-top:1px solid var(--rule);
  position:relative;
}
.offerings .group-label{margin-top:0}
.offer-grid{display:grid;gap:var(--s5);grid-template-columns:repeat(auto-fit,minmax(min(100%,17rem),1fr))}
.offer h3{
  font-family:var(--font-display);
  font-weight:300;
  font-size:var(--t-title);
  line-height:1.2;
  border-top:1px solid var(--rule);
  padding-top:var(--s2);
}
.offer .cadence{
  margin-top:var(--s1);
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--accent-ink);
  font-weight:500;
}
.offer p{margin-top:var(--s2);font-size:var(--t-small);line-height:1.7;color:var(--ink)}

/* ---- 13. V · ΒΡΕΣ ΜΑΣ · the letter -------------------------------------- */
.arrival{
  display:grid;
  grid-template-columns:1fr minmax(0,26rem);
  gap:var(--s7) var(--s6);
  align-items:start;
}
.letter{max-width:34rem}
.field{
  display:block;
  margin-bottom:var(--s4);
  position:relative;
}
/* The caption is a span inside the label, because the label IS .field and
   wraps its own input. There used to be a `.field label` rule here styling a
   nested label that does not exist, so the captions have always rendered as
   plain body copy. They read well at that size, so this keeps what ships and
   drops the rule that never applied. */
.field > span{
  display:block;
  color:var(--ink-dim);
  margin-bottom:var(--s1);
}
.field input,.field textarea{
  width:100%;
  background:none;
  border:0;
  border-bottom:1px solid var(--rule-strong);
  color:var(--ink);
  font:inherit;
  font-size:var(--t-lead);
  padding:.55rem 0;
  border-radius:0;
  transition:border-color .35s ease;
}
.field textarea{resize:vertical;min-height:6.5rem;line-height:1.6}
.field input:focus,.field textarea:focus{
  outline:none;
  border-bottom-color:var(--accent);
}
.send{
  padding:.95rem 2.2rem;
  border:1px solid var(--rule-strong);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  font-weight:500;
  transition:background .35s ease,color .35s ease,border-color .35s ease;
}
/* hover in motion.css, shared with the rail and the other two CTAs */
.form-note{margin-top:var(--s2);font-size:var(--t-micro);letter-spacing:.1em;color:var(--ink-mute);text-transform:none}

.details dl{border-top:1px solid var(--rule)}
.details .pair{
  display:flex;justify-content:space-between;gap:var(--s3);
  padding:var(--s2) 0;border-bottom:1px solid var(--rule);
  font-size:var(--t-small);
}
.details .pair dt{color:var(--ink-mute)}
.details .pair--head{border-top:0}
.details .pair dd{font-variant-numeric:tabular-nums;text-align:right}
.details .block{margin-top:var(--s5)}
.details .block h3{
  font-size:var(--t-micro);letter-spacing:.16em;text-transform:uppercase;
  color:var(--accent-ink);font-weight:500;margin-bottom:var(--s2);
}
.details a.line{
  display:block;padding:.4rem 0;border-bottom:1px solid var(--rule);
  font-size:var(--t-small);transition:color .3s ease,padding-left .3s ease;
}
.details a.line:hover{color:var(--accent-ink);padding-left:.5rem}
@media (max-width:900px){
  .arrival{grid-template-columns:1fr;gap:var(--s6)}
}

/* ---- 14. footer --------------------------------------------------------- */
.foot{
  border-top:1px solid var(--rule);
  padding:var(--s6) var(--pad-x) var(--s4);
  margin-top:var(--s6);
}
.foot-top{
  display:grid;
  grid-template-columns:1.4fr 1fr 1.2fr 1fr;
  gap:var(--s5) var(--s4);
  padding-bottom:var(--s5);
}
.foot-brand .foot-wm{display:block;width:132px;margin-bottom:var(--s3)}
.foot-brand .foot-wm svg{width:100%;height:auto}
/* Quicksand Light. Measured against the tagline outlines in lockup.svg, 300 is
   the weight the logo was set in: it matches on width and height together,
   where 400/500/600 drift 6-9% wide. */
.foot-tag{
  font-family:var(--font-tagline);
  font-weight:300;
  font-size:1rem;
  letter-spacing:.015em;
  color:var(--ink-dim);
  margin-bottom:var(--s2);
}
.foot-line{font-size:var(--t-small);line-height:1.65;color:var(--ink-mute);max-width:34ch}

.foot-col h3{
  font-size:var(--t-micro);
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--accent-ink);
  font-weight:500;
  margin-bottom:var(--s3);
}
.foot-nav{display:flex;flex-direction:column}
.foot-nav a{
  display:flex;align-items:baseline;gap:.7rem;
  padding:.4rem 0;
  font-size:var(--t-small);
  color:var(--ink-dim);
  transition:color .3s ease,padding-left .3s ease;
}
.foot-nav a .n{
  font-family:var(--font-display);
  font-size:.62rem;color:var(--ink-mute);flex:0 0 2ch;
}
.foot-nav a:hover{color:var(--ink);padding-left:.4rem}

.foot-link{
  display:block;
  padding:.4rem 0;
  font-size:var(--t-small);
  color:var(--ink-dim);
  transition:color .3s ease;
}
.foot-link:hover{color:var(--accent-ink)}
.foot-social{display:flex;gap:var(--s3);margin-top:var(--s2)}
.foot-social a{
  font-size:var(--t-micro);letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-mute);transition:color .3s ease;
}
.foot-social a:hover{color:var(--accent-ink)}

.foot-hours .pair{
  display:flex;justify-content:space-between;gap:var(--s2);
  padding:.4rem 0;
  font-size:var(--t-small);
  color:var(--ink-dim);
  border-bottom:1px solid var(--rule);
}
.foot-hours .pair dd{font-variant-numeric:tabular-nums;color:var(--ink-mute)}

.foot-base{
  border-top:1px solid var(--rule);
  padding-top:var(--s3);
  display:flex;flex-wrap:wrap;gap:var(--s3);
  justify-content:space-between;
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-mute);
}
@media (max-width:1080px){
  .foot-top{grid-template-columns:1fr 1fr}
}
@media (max-width:620px){
  .foot-top{grid-template-columns:1fr;gap:var(--s4)}
}

/* ---- 15. lightbox ------------------------------------------------------- */
.lightbox{
  position:fixed;inset:0;z-index:80;
  background:#1c1c1c;
  display:grid;place-items:center;
  padding:var(--s4);
  visibility:hidden;
}
.lightbox[data-open="true"]{visibility:visible}
.lightbox figure{max-width:min(94vw,1100px);max-height:86svh}
.lightbox img{max-height:76svh;width:auto;object-fit:contain}
.lightbox figcaption{
  margin-top:var(--s2);font-size:var(--t-micro);letter-spacing:.2em;
  text-transform:uppercase;color:var(--accent-ink);
}
.lightbox .close{
  position:absolute;top:var(--s3);right:var(--s3);
  font-size:var(--t-micro);letter-spacing:.2em;text-transform:uppercase;
  color:var(--stone);padding:var(--s2);
}
.lightbox .nav-btn{
  position:absolute;top:50%;transform:translateY(-50%);
  padding:var(--s3);color:var(--stone);opacity:.6;transition:opacity .3s
}
.lightbox .nav-btn:hover{opacity:1}
.lightbox .prev{left:var(--s1)} .lightbox .next{right:var(--s1)}

/* ---- 16. skip link ------------------------------------------------------ */
.skip-link{
  position:absolute;left:-9999px;top:0;z-index:100;
  background:var(--stone);color:var(--charcoal);
  padding:var(--s2) var(--s3);font-size:var(--t-micro);
  letter-spacing:.2em;text-transform:uppercase;
}
.skip-link:focus{left:var(--s2);top:var(--s2)}


/* ---- 17. multi-page: the chapter head at the top of an inner page ------- */
/* On a five-page site each chapter opens with its own masthead instead of a
   hero. The numeral is the reader's position on the route, not decoration. */
.page-head{
  padding:calc(var(--s8) + 2vw) var(--pad-x) var(--s6);
  border-bottom:1px solid var(--rule);
  max-width:none;
}
.page-head .mark{
  display:block;
  font-size:var(--t-micro);
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--accent-ink);
  margin-bottom:var(--s3);
}
.page-head h1{
  font-family:var(--font-display);
  font-weight:200;
  font-size:var(--t-chapter);
  line-height:1.0;
  letter-spacing:-.025em;
  max-width:16ch;
}
.page-head .gloss{
  display:block;
  font-size:var(--t-micro);
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--ink-mute);
  font-weight:500;
  margin-top:var(--s2);
}
.page-head .lead{margin-top:var(--s4)}

.chapter--tight{padding-top:var(--s6)}
main > .chapter:first-of-type,
main > .page-head:first-child{border-top:0}

/* ---- 18. the pager: the route stays linear across pages ---------------- */
/* Full width, deliberately: the complaint about this block was its HEIGHT, not
   its width. It spans the column and flexes with it, but the vertical padding
   is now a third of what it was. */
.pager{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:var(--s3);
  padding:var(--s3) var(--pad-x) var(--s4);
  border-top:1px solid var(--rule);
}
.pager-link{
  display:flex;
  flex-direction:column;
  gap:.2rem;
  padding:var(--s2) 0 0;
  transition:color .35s ease;
}
/* placed by column rather than by order, so the first chapter can leave the
   left half empty without an empty element holding it open */
.pager-prev{grid-column:1}
.pager-next{grid-column:2;align-items:flex-end;text-align:right}
.pager-link .dir{
  font-size:var(--t-micro);
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--ink-mute);
}
.pager-link .ttl{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.25rem,2.4vw,1.75rem);
  line-height:1.1;
  letter-spacing:-.015em;
  display:flex;
  align-items:baseline;
  gap:var(--s2);
}
.pager-link .ttl .n{
  font-family:var(--font-text);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  color:var(--accent-ink);
  font-weight:500;
}
.pager-link:hover .ttl{color:var(--accent-ink)}
@media (max-width:640px){
  .pager{grid-template-columns:1fr}
  .pager-prev,.pager-next{grid-column:auto}
  .pager-next{align-items:flex-start;text-align:left}
}

/* ---- 19. placeholders, until the photographs land ---------------------- */
.guide .portrait .empty,
.expedition .plate-lg .empty,
.range-plate .empty{
  position:absolute; inset:0;
  display:grid; place-items:center;
  border:1px solid var(--rule);
  color:var(--ink-mute);
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  text-align:center;
  padding:var(--s2);
}

/* ---- 20. section backgrounds ------------------------------------------- */
/* A landscape photograph behind a whole section. The scrim is not decoration:
   without it the display type loses contrast wherever the frame is bright. */
.has-bg{
  position:relative;
  isolation:isolate;
}
/* No overflow here: it would turn this into a scroll container and kill the
   sticky frame inside it. The frame does the clipping instead. */
.section-bg{
  position:absolute;
  inset:0;
  z-index:-1;
  pointer-events:none;
}
/* The frame is one viewport tall and sticks, so a tall section does not stretch
   a 1080px photograph over 1900px of height, and the image holds still while
   the text travels over it. */
/* min(viewport, section): a chapter opener is shorter than the screen, so it
   takes its own height and the frame fills it exactly with nothing wasted; a
   long section takes the viewport height and sticks, so a 1350px photograph is
   never stretched down 1900px of page. */
.section-bg .frame{
  position:sticky;
  top:0;
  height:min(100svh, 100%);
  overflow:hidden;
}
.section-bg img{
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:center 34%;
}
/* charcoal scrim, heavier at the left where the text column sits */
.section-bg .frame::after{
  content:'';
  position:absolute; inset:0;
  background:
    linear-gradient(to right,
      rgba(43,43,43,.94) 0%,
      rgba(43,43,43,.86) 42%,
      rgba(43,43,43,.66) 100%),
    linear-gradient(to bottom,
      var(--charcoal) 0%,
      rgba(43,43,43,0) 18%,
      rgba(43,43,43,0) 82%,
      var(--charcoal) 100%);
}
/* The timetable is the one section whose content runs the FULL width at every
   size: a ledger row puts the class name on the left and the teacher and the
   level marks hard against the right, which is precisely where the gradient
   above is thinnest. So this section gets the even scrim the phone already
   uses, and a heavier one, because the text over it is the smallest on the
   page.

   The wash is .89 rather than the .84 it began at. The photograph behind the
   table is now centred on its subject instead of sitting on the empty floor
   above it (see `timetable` in content.js), which puts two lit bodies under
   the rows where there used to be bare boards. At .84 that dropped the teacher
   name to 4.45:1, under the 4.5 an 12.8px line needs; .89 buys it back.

   Worst case at .89, measured at the 95th percentile of brightness across
   1366-2560px and every day length the table takes: class name 8.2:1, time
   5.0:1, teacher 4.9:1. Swap the photograph, or move its crop, and those
   numbers change; measure them again rather than trusting these. */
#timetable .section-bg .frame::after{
  background:
    linear-gradient(rgba(43,43,43,.89),rgba(43,43,43,.89)),
    linear-gradient(to bottom,var(--charcoal) 0%,rgba(43,43,43,0) 18%,
                    rgba(43,43,43,0) 82%,var(--charcoal) 100%);
}

@media (max-width:860px){
  /* on a phone the text runs the full width, so the scrim has to be even */
  .section-bg .frame::after{
    background:
      linear-gradient(rgba(43,43,43,.9),rgba(43,43,43,.9)),
      linear-gradient(to bottom,var(--charcoal) 0%,rgba(43,43,43,0) 22%,
                      rgba(43,43,43,0) 78%,var(--charcoal) 100%);
  }
}


/* ---- 21. voices -------------------------------------------------------- */
/* The score leads, not the prose. Set as ruled rows like everything else, the
   quotes read as more body copy; a large aggregate and drawn stars say "these
   are reviews" before a word is read. */
.voices-title{
  font-family:var(--font-display);
  font-weight:200;
  font-size:var(--t-chapter);
  line-height:1.02;
  letter-spacing:-.022em;
  max-width:16ch;
}

.voices-score{
  display:flex;
  align-items:baseline;
  gap:var(--s3);
  flex-wrap:wrap;
  margin:var(--s4) 0 var(--s6);
  padding-bottom:var(--s4);
  border-bottom:1px solid var(--rule);
}
.score-num{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(3.4rem,8vw,6rem);
  line-height:.9;
  letter-spacing:-.04em;
  color:var(--ink);
}
.score-stars{display:flex;gap:4px;align-self:center}
/* On the row's baseline this label dropped to the foot of a 96px numeral and
   hung below the stars it belongs to, reading as a third thing rather than as
   their caption. Centred, it sits level with them. */
.score-src{
  align-self:center;
  font-size:var(--t-micro);
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--ink-mute);
}
.star{fill:var(--rule-strong);flex:none}
.star.on{fill:var(--accent)}

/* each review on its own lifted panel: not a bordered card, just a shade off
   the page ground, which is enough to lift it out of the reading column */
.voices{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,17rem),1fr));
  gap:var(--s3);
}
.voice{
  background:#313131;
  padding:var(--s4);
  display:flex;
  flex-direction:column;
  gap:var(--s3);
}
.voice-stars{display:flex;gap:3px}
.voice blockquote{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.02rem,1.5vw,1.24rem);
  line-height:1.45;
  letter-spacing:-.008em;
  color:var(--ink);
  flex:1;
}
.voice figcaption{
  display:flex;
  align-items:baseline;
  gap:var(--s2);
  padding-top:var(--s2);
  border-top:1px solid var(--rule);
}
.voice .who{font-size:var(--t-small);color:var(--ink)}
.voice .when{
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--ink-mute);
  margin-left:auto;
}

/* ---- 21b. the consent notice -------------------------------------------
   A fork in the path, not a cookie bar. It sits on the page ground with one
   hairline above it, the same rule every section on this site is divided by,
   rather than arriving as a floating card with a shadow and a radius: nothing
   else here is a card, and a notice that looks bolted on is one.

   It clears the rail, because the rail is fixed and the notice must not cover
   the route. Below 1080px --rail is 0 and it simply runs the full width. */
.consent{
  position:fixed;
  left:var(--rail); right:0; bottom:0;
  z-index:58;                       /* under the grain and vignette, over all else */
  background:var(--ground);
  border-top:1px solid var(--rule-strong);
  padding:var(--s4) var(--pad-x);
}
.consent-inner{
  display:grid;
  grid-template-columns:auto minmax(0,1fr) auto;
  gap:var(--s4);
  align-items:center;
  max-width:calc(var(--measure) + 34rem);
}
/* The band takes focus when it arrives so a screen reader announces it, and it
   is not an interactive thing, so it does not draw a ring for that. A keyboard
   user tabbing to a CHOICE still gets one: this only suppresses the container's
   own. */
.consent:focus{outline:none}
.consent-mark{color:var(--ink-mute);flex:none}
.consent-fork{width:40px;height:46px;display:block}
/* The stem is the route arriving. The branches start quiet and the one under
   the pointer lights, so the mark answers the choice rather than decorating
   it. :has() means no JS is involved in it at all. */
.consent-fork .branch-l,.consent-fork .branch-r,
.consent-fork .dot-l,.consent-fork .dot-r{
  transition:stroke .35s ease,opacity .35s ease;
}
.consent:has(.c-essential:hover) .branch-l,
.consent:has(.c-essential:focus-visible) .branch-l,
.consent:has(.c-essential:hover) .dot-l,
.consent:has(.c-essential:focus-visible) .dot-l,
.consent:has(.c-all:hover) .branch-r,
.consent:has(.c-all:focus-visible) .branch-r,
.consent:has(.c-all:hover) .dot-r,
.consent:has(.c-all:focus-visible) .dot-r{stroke:var(--accent)}
.consent:has(.c-essential:hover) .branch-r,
.consent:has(.c-essential:focus-visible) .branch-r,
.consent:has(.c-essential:hover) .dot-r,
.consent:has(.c-essential:focus-visible) .dot-r,
.consent:has(.c-all:hover) .branch-l,
.consent:has(.c-all:focus-visible) .branch-l,
.consent:has(.c-all:hover) .dot-l,
.consent:has(.c-all:focus-visible) .dot-l{opacity:.3}

.consent-title{
  font-family:var(--font-display);
  font-weight:200;
  font-size:clamp(1.15rem,1.9vw,1.5rem);
  line-height:1.15;
  letter-spacing:-.015em;
  color:var(--ink);
}
.consent-body{
  margin-top:.45rem;
  font-size:var(--t-small);
  line-height:1.6;
  color:var(--ink);
  max-width:62ch;
}
.consent-note{
  margin-top:.5rem;
  font-size:var(--t-micro);
  letter-spacing:.1em;
  color:var(--ink-mute);
}
/* Both choices carry the same weight. Under the GDPR refusing has to be as
   easy as accepting, and a quiet secondary link would fail that as well as
   reading as a nudge. Same treatment as every other action on the site: see
   the primary-action block in motion.css. */
.consent-choices{display:flex;gap:var(--s2);flex-wrap:wrap}
.consent-btn{
  padding:.75rem 1.35rem;
  border:1px solid var(--rule-strong);
  font-size:var(--t-micro);
  letter-spacing:.16em;
  text-transform:uppercase;
  font-weight:500;
  color:var(--ink);
  white-space:nowrap;
}
/* the footer control that brings it back */
.foot-privacy{
  font:inherit;
  letter-spacing:inherit;
  text-transform:inherit;
  color:var(--ink-mute);
  border-bottom:1px solid var(--rule);
  padding-bottom:.15rem;
  transition:color .3s ease,border-color .3s ease;
}
.foot-privacy:hover{color:var(--ink);border-bottom-color:var(--accent)}

/* On a phone this ran to 406px, very nearly half the screen, which is what a
   stock cookie bar does and the reason everybody has learned to swat them away
   without reading. The type comes down, the note comes off (the footer control
   is still there and the desktop copy still points at it), and the choices sit
   on one row: they are short enough in both languages to share it. */
@media (max-width:860px){
  .consent{padding:var(--s3) var(--pad-x)}
  .consent-inner{
    grid-template-columns:auto minmax(0,1fr);
    gap:var(--s2) var(--s3);
    align-items:start;
  }
  .consent-title{font-size:1.05rem}
  .consent-body{margin-top:.3rem;font-size:.82rem;line-height:1.5}
  .consent-note{display:none}
  /* the choices drop under the copy and span both columns, so neither button
     is squeezed into a column narrower than its own words */
  .consent-choices{grid-column:1 / -1;gap:.6rem}
  .consent-btn{
    flex:1 1 0;
    text-align:center;
    padding:.7rem .5rem;
    font-size:.68rem;
    letter-spacing:.1em;
    white-space:normal;
  }
  .consent-fork{width:26px;height:30px}
}

/* ---- 22. print ---------------------------------------------------------- */
@media print{
  .grain,.vignette,.trail,.topbar,.prologue,#wander,.cursor-trail,
  .gate-overlay,.pager,.section-bg{display:none!important}
  body{background:#fff;color:#000}
}
