/* The shell every page shares: the ink, the band, the gutters and the type.

   This was inline in index.html while index.html was the whole site. With a
   speakers page and a registration page per stop it is three copies or one
   file, and three copies of a header band drift within a week. index.html
   still carries its own <style> for the things only it has — the corridor, the
   hero card, the sponsor plate, the swipe. */

/* ---------- type ----------
   IBM Plex Mono for titles, labels and buttons; Inter for subheads and body.
   Both are open-source (SIL OFL) and served by Google Fonts from the <link> in
   each page's head. They replace Forma DJR Mono and HP Forma DJR Office, which
   were self-hosted from desktop files without a web licence. */

/* The blue is the logo's own — the asset is a single colour, #024AD8, so the
   mark, the type and the bars in the field are all literally one ink. */
:root{
  --blue:#024AD8;
  /* Blue is now a role, not the whole palette: headings, calls to action, the
     band, the seam and the footer panel. Everything a reader actually reads is
     ink or grey, which is what lets the blue mean something when it appears. */
  --ink:#111318;
  --slate:#63818E;       /* the swatch's third colour: subheads, captions,
                            notes, anything subordinate to the body */
  --ground:#ffffff;      /* the ground the corridor sits on */
  --paper:#ffffff;
  --bar-h:60px;
  --edge:clamp(16px,3.2vw,46px);
  --hero-x:clamp(20px,8vw,118px);
  --card-w:min(580px,47vw);
  /* The type stack.

       title    mono, medium      the big lines and every section heading
       subhead  micro, medium     the line under a title
       body     text, regular     anything anyone actually reads at length
       caption  mono, regular     labels, dates, status, nav, buttons

     mono is IBM Plex Mono; micro and text are both Inter. */
  /* One plate, two spellings of it: the bordered block and the bare one have
     to put their headings on the same vertical axis, and they only do that
     while the bare one's padding is the other's padding plus its border. */
  /* One stroke for every box on the page. It was 3px on the plates, 2px on the
     schedule cards and 1px on everything else, which read as three unrelated
     systems of box rather than one. */
  --bd:2px;
  --plate-pad:clamp(22px,3vw,42px);
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --micro:"Inter",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;
  --text:"Inter",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;
}
*{box-sizing:border-box}
/* overscroll-behavior kills the rubber band. It matters more here than on an
   ordinary page: the corridor is a FIXED canvas, so bouncing past the end of
   the document does not reveal the body's background — it reveals the white
   corridor still sitting there behind the blue footer, which reads as the
   footer having come unstuck. html gets the ink as a second line of defence
   for any browser that bounces anyway. */
html{scroll-behavior:smooth;overscroll-behavior:none;background:var(--blue)}
body{overscroll-behavior:none}
/* The page reads in the display face. The mono is kept as a deliberate voice
   rather than the default one: labels, dates, status, navigation, buttons —
   the machine-side of the page. Anything set in it keeps the negative
   word-spacing those labels were tuned with; anything set in the display face
   must not, because that tracking was compensating for a monospace's fixed
   advance and in a proportional face it just crashes the words together. */
/* Body copy is Regular, per the stack — 400, not the 500 everything else runs
   at. It was medium for a while and read heavy at 13-16px, which is the usual
   reason a text cut and a display cut are different fonts in the first place:
   the weight that carries a 40px title is too much ink in a paragraph. Titles,
   subheads and the mono labels stay at 500. */
body{
  margin:0;background:var(--ground);color:var(--ink);
  font-family:var(--text);font-weight:400;-webkit-font-smoothing:antialiased;
  /* A hair of tracking on the display face. Forma sets tight by default, which
     is right at a headline's size and closes the words up at a paragraph's — the
     same reason the titles carry NEGATIVE tracking a few rules down. Everything
     with its own letter-spacing (the mono labels, the headings, the nav, the
     notes) is unaffected; this is only the default. */
  letter-spacing:.012em;
}
/* One weight across the whole site. The browser ships h1-h6, b, strong and
   button at bold, so inheriting medium from body is not enough — they have to
   be brought down explicitly or the page runs at two weights by accident. */
h1,h2,h3,h4,h5,h6,b,strong,button{font-weight:500}
input,select,textarea{font-weight:400}
/* Every title is mono, so every title keeps the negative word-spacing the mono
   was tuned with. Body and subheads are proportional and must NOT have it —
   that tracking was compensating for a monospace's fixed advance, and in a
   proportional face it just crashes the words together. */
h1,h2,.rcard__name,.spk__name{
  font-family:var(--mono);font-weight:500;word-spacing:-.3em;color:var(--blue);
}


/* ---------- header ----------
   A solid band of the ink across the top, the way the reference cuts the
   corridor off rather than fading it out. */
.bar{
  position:fixed;top:0;left:0;right:0;height:var(--bar-h);z-index:3;
  display:flex;align-items:center;justify-content:space-between;
  padding:0 var(--edge);background:var(--blue);color:#fff;
}
.brand{display:flex;align-items:center;text-decoration:none;color:inherit}
/* One asset. The mark, the wordmark, the leading between its two lines and the
   space between mark and type are all inside the SVG, so the lockup cannot
   drift out of its own proportions the way a mark-plus-span pair can. Its paths
   ship white, which is why the filter that used to knock the PNG out is gone.
   Height is the only handle here — width follows the 553:140 box. */
.brand img{height:36px;width:auto;display:block}

.nav{display:flex;align-items:center;gap:clamp(14px,2vw,26px)}
.nav a{
  font-family:var(--mono);font-size:11px;letter-spacing:.0375em;word-spacing:-.3em;font-weight:500;
  color:#fff;text-decoration:none;white-space:nowrap;opacity:.78;
  transition:opacity .18s ease;
}
.nav a:hover,.nav a[aria-current]{opacity:1}
/* The current page is marked by a rule under it rather than by a colour: there
   is only one ink on the band and it is the band. */
.nav a[aria-current]{box-shadow:0 3px 0 -1px #fff}
.host{
  font-family:var(--mono);font-size:11px;letter-spacing:.0375em;word-spacing:-.3em;font-weight:500;color:#fff;text-decoration:none;
  border:1px solid #fff;padding:6px 12px;opacity:1;
  transition:background .18s ease,color .18s ease;
}
.host:hover{background:#fff;color:var(--blue)}
.host[aria-current]{background:#fff;color:var(--blue);box-shadow:none}
.host[aria-expanded="true"]{background:#fff;color:var(--blue)}

/* ---------- the register menu ----------
   Hangs off the band, which is fixed, so the panel is fixed with it. Every rule
   here has to out-specify .nav a above — the menu lives inside .nav, so its
   links inherit the band's white-on-blue and would be invisible on paper.

   Flush under the button, no gap: the panel opens on hover, and a gap between
   the two is a strip the pointer crosses where nothing is hovered. */
.nav__wrap{position:relative}
.navdrop{
  position:absolute;top:100%;right:0;z-index:4;min-width:250px;
  background:var(--paper);border:var(--bd) solid var(--blue);padding:8px;
  max-height:min(72vh,540px);overflow:auto;
  /* no drop shadow: the panel already separates from the page by being white
     inside a 2px rule, and a soft blue bloom under a hard-edged box is the one
     piece of depth on a site whose depth is drawn in perspective */
}
.navdrop a{
  display:block;padding:9px 12px;text-decoration:none;
  /* blue at rest, not ink: these are navigation, and blue is what the site uses
     for anything you can click */
  color:var(--blue);opacity:1;white-space:nowrap;
  font-family:var(--text);font-weight:400;font-size:13px;
  letter-spacing:0;word-spacing:0;
}
.navdrop a:hover,.navdrop a:focus-visible{background:var(--blue);color:#fff;outline:none}

/* ---------- the field ----------
   One canvas behind everything, sized to the window in CSS pixels; field.js
   handles the device-pixel scaling itself. On the index it flies and then
   freezes; on a sub-page it opens already frozen, because there is no hero to
   fly through — what the two share is the still wireframe the plates sit on. */
#field{position:fixed;inset:0;width:100%;height:100%;display:block;z-index:0;
  -webkit-user-select:none;user-select:none}

/* ---------- the sheet and the sub-pages ----------
   index.html arrives at its sheet by swiping it up over the corridor; a
   sub-page simply is the sheet, opened at the top. Same gutters, same type,
   so moving between them is moving within one surface. */
/* Symmetric. The lopsided version — hero-x on the left, edge on the right —
   came from when this was a left-aligned column of type under a left-aligned
   hero card. The blocks are centred plates now, and a plate with 115px of gutter
   on one side and 46px on the other just reads as misaligned. */
.night__in,.page__in{padding:clamp(48px,6vw,86px) var(--hero-x) 0}
/* No bottom padding: the footer is the last thing on every page and bleeds to
   the bottom edge. Its own margin-top is the gap above it. */
.page{position:relative;z-index:2;min-height:100vh;padding:var(--bar-h) 0 0}
/* Each block is a plate, not a stretch of page. On the index that is what lets
   the corridor show between them; everywhere else it keeps the two surfaces
   reading as one system. The border is heavy on purpose — a hairline plate on a
   hairline field is two drawings at the same weight and neither wins. */
.blk{background:var(--paper);border:var(--bd) solid var(--blue);
  padding:clamp(24px,3.2vw,44px) var(--plate-pad) clamp(28px,3.6vw,50px)}
/* The gap between sections. It was tuned when the plates were a stack of white
   boxes with the corridor showing between them, where a small gap reads as a
   seam rather than as a missing one; with the band, the schedule's grid and the
   reels in the run, the same number ran everything together. Half again, and the
   two rules below keep their proportion to it. */
.blk + .blk{margin-top:clamp(42px,5vw,74px)}
/* The schedule is already a grid of plates. Wrapping them in one more is a
   plate of plates — two borders doing the same job, the outer one just making
   the inner ones look inset. Only the heading sits loose on the corridor, and
   the cards below are opaque enough to carry the section on their own. */
/* The bare block has no padding, so its cards sit on exactly the margin the
   bordered plate's OUTER edge sits on — the boxes line up down the page whatever
   is inside them. The heading alone is pushed in by the plate's own inset, so
   it still shares an axis with the headings inside the plates. Padding the
   whole block would align the headings and misalign every card by the same
   amount; this way both land. */
.blk--bare{background:none;border:0;padding:0}
/* No inset on the heading either: everything in a bare block — heading and cards
   alike — sits on the page's left margin, the same line the bordered plate's
   outer edge sits on. The heading was pushed in to share an axis with the
   headings inside the plates, which lined up the type but left every heading
   hanging over its own cards. */
/* More than between two plates: a bare block ends on its own content — cards, a
   grid of reels — with no border to close it, so the gap is the only thing
   saying the section is over. */
.blk--bare + .blk{margin-top:clamp(54px,6.4vw,92px)}
/* anchored from the band's menu, so they must not land under the fixed band */
.blk[id],.rcard{scroll-margin-top:calc(var(--bar-h) + 22px)}

.night h2,.page h2{margin:0;font-size:clamp(23px,3.1vw,42px);line-height:1.12;
  letter-spacing:-.03em;max-width:20ch}
/* A block whose contents are centred in the sheet rather than hung off its left
   edge, for the one section held to a column narrower than the sheet: the
   heading has to come with the grid under it, or the two sit on different axes
   and the block reads as half-aligned. The 20ch measure above is what keeps a
   centred heading from being centred in a box of its own. */
.blk--mid h2{max-width:none;text-align:center}
/* ---------- the laptop band ----------
   The one thing on this page a reader can WIN, given the width of the sheet and
   the ink instead of the paper. It is a band rather than a plate because it is
   an offer rather than a passage: no border, no white, nothing about it shaped
   like the reading above it.

   The drawing is the same laptop being given away, in the same hairline the
   corridor is drawn in — one ink, one line weight, which is what keeps it from
   reading as a stock photograph dropped into a layout. It bleeds off the left
   edge on purpose: cropped, it is a thing passing through the band; contained,
   it is a product shot.

   Two columns of a grid rather than a background image, so the art has a real
   width the type is laid out against. The art column is the smaller half — the
   words are what has to be read. */
.promo{
  display:grid;grid-template-columns:minmax(0,.44fr) minmax(0,.56fr);
  align-items:center;
  margin-top:clamp(26px,3.2vw,46px);
  background:var(--blue);color:#fff;overflow:hidden;
}
/* Stretched and cropped rather than fitted: the drawing carries its own field of
   blue, the same blue as the band, so what the crop leaves is a laptop coming in
   from off the page and no seam where one ends and the other begins. */
/* Clipped to its own column, not just to the band: the drawing is scaled past
   the cell so the laptop arrives at a size worth looking at, and without this it
   runs straight under the first two words of every line of the copy. */
.promo__art{position:relative;align-self:stretch;overflow:hidden;
  min-height:clamp(200px,22vw,360px)}
/* The drawing needs air around it as much as the type does. Sized off the band's
   height rather than fitted to it: at 100% the laptop touches the top and bottom
   edges and the band reads as a crop of a bigger picture instead of a thing with
   a drawing in it.

   Flush to the LEFT edge, though, not centred. The export is cropped — the
   keyboard runs off the side of its own canvas and one line stops in mid-air —
   and centred, that crop lands in the middle of the band as a hard vertical cut
   with blue on both sides of it, which reads as a rendering fault. Sitting the
   canvas's cut edge on the band's own edge is the difference between a drawing
   that has been damaged and one that carries on past the page. */
.promo__art img{
  position:absolute;top:50%;left:0;transform:translateY(-50%);
  /* Sized off the column's WIDTH, not the band's height: the drawing's own
     empty margins have been trimmed out of its viewBox, so its box is the
     laptop, and letting it run the width of the column is what closes the gap
     between it and the copy. max-height is the guard for a very wide column —
     nothing here may crop the drawing. */
  width:97%;height:auto;max-height:86%;max-width:none;display:block;
}
.promo__body{padding:clamp(38px,4.4vw,68px) clamp(30px,4.4vw,72px)}
/* The band's own floor and ceiling. A band this wide with the copy tight against
   its edges reads as a strip that has been cut out of something; the room is
   what makes it a panel. */
.promo{min-height:clamp(300px,30vw,500px)}
/* Nothing in site.css pairs an <aside> with the block after it, so the schedule
   was sitting straight on the band's bottom edge — a heading with no room above
   it, against a full-width field of ink. It takes more than the gap between two
   plates, not less. */
.promo + .blk{margin-top:clamp(64px,7.4vw,108px)}
/* The title carries the band on its own, so it is sized against the section
   headings rather than against the copy under it — a step below an h2, which is
   what an offer inside a page should be. */
.promo__title{
  margin:0 0 clamp(18px,1.8vw,26px);
  font-family:var(--mono);font-weight:500;word-spacing:-.3em;
  font-size:clamp(21px,2.5vw,38px);line-height:1.12;letter-spacing:-.02em;
}
.promo p{margin:0 0 1.05em;font-size:14px;line-height:1.7;color:rgba(255,255,255,.88);
  max-width:62ch}
/* The quiz's own title inside the sentence. On the ink it cannot be blue, so it
   is the one thing in the band at full white. */
.promo em{font-style:normal;font-weight:500;color:#fff}
/* Same voice as the disclaimers on the plates — mono, subordinate — inverted. */
/* `.promo p` is a class AND a type, so it outranks a bare class and this rule
   had been losing to it silently — the disclaimer was sitting at the copy's own
   colour. Two classes settles it. */
.promo .promo__note{
  margin:clamp(18px,2vw,28px) 0 0;
  font-family:var(--mono);font-weight:500;
  font-size:11.5px;line-height:1.6;letter-spacing:.005em;word-spacing:-.2em;
  /* Quieter than the copy above it by a wide margin. A disclaimer is a
     condition on the offer, not part of it — it has to be legible to someone
     who goes looking and invisible to everyone else. */
  color:rgba(255,255,255,.5);max-width:76ch;
}
.promo p:last-child{margin-bottom:0}
/* No single-word last lines. text-wrap:pretty is the fix rather than hand-placed
   &nbsp; before the last word: the browser reflows the closing lines to avoid an
   orphan, and it keeps working when the copy or the column width changes, which
   a hard-coded non-breaking space does not. Degrades to normal wrapping where
   it is unsupported. */
p,.spk__bio,.check span,.sent{text-wrap:pretty}


/* ---------- speakers ----------
   Slots, not people. Everything a real entry needs is drawn empty rather than
   filled with a plausible stranger — a placeholder that looks like content is
   the one kind that ships by accident. */
/* Five, explicitly. auto-fill decides the count from the available width, which
   would be right if the count were open-ended, but the bill is five people
   and it reads
   as one row or it reads as a ragged grid. It steps down by breakpoint instead
   of by whatever happens to fit. */
.spk{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));
  gap:clamp(12px,1.4vw,20px);margin-top:clamp(30px,4.5vw,54px)}
/* Its own white ground. While these sat inside a bordered plate the plate was
   the background; without one the corridor comes straight through the card and
   the hatch, and the type ends up on wireframe. .rcard carries its own for the
   same reason. */
.spk__card{background:var(--paper);border:var(--bd) solid var(--blue);
  display:flex;flex-direction:column}
/* the hatch is the empty state; a filled plate drops it or it shows through the
   portrait's own transparent edges */
.spk__photo.has-photo{background:var(--paper)}
.spk__photo{position:relative;aspect-ratio:4/5;border-bottom:var(--bd) solid var(--blue);
  display:flex;align-items:flex-end;padding:12px;
  background:repeating-linear-gradient(135deg,rgba(2,74,216,.045) 0 9px,transparent 9px 18px)}
.spk__photo img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.spk__photo span{font-family:var(--mono);font-size:10px;letter-spacing:.0375em;word-spacing:-.3em;font-weight:500;color:var(--slate)}
.spk__body{padding:clamp(14px,1.6vw,20px);display:flex;flex-direction:column;gap:9px;flex:1}
.spk__name{font-size:16px;letter-spacing:-.02em;margin:0}
.spk__venue{font-family:var(--micro);font-weight:500;font-size:13.5px;line-height:1.4;
   margin:0;color:var(--slate)}
.spk__role{font-family:var(--micro);font-weight:500;font-size:13.5px;line-height:1.4;
  margin:0;color:var(--slate)}
.spk__bio{font-family:var(--text);font-size:13.5px;line-height:1.6;margin:0;color:var(--ink);flex:1}
.spk__more{align-self:flex-start;margin:0;padding:0;border:0;background:none;cursor:pointer;
  font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.06em;color:var(--blue);
  text-decoration:underline;text-underline-offset:3px}
/* An unfilled field is a labelled gap, not blank space: you can see which one
   is missing without opening tour.js. */
.tbc{opacity:.42}
.spk__dot{opacity:.42}

/* Three cards side by side, each a column: logo on top over the same hairline
   the register list divides its rows with, inset to the card's padding, then
   company, workshop and brief. Cards in a row stretch to the tallest so the borders
   line up. */
.workshop__speakers{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
   gap:clamp(16px,2vw,28px);margin-top:clamp(30px,4.5vw,54px)}
.workshop__speaker{display:flex;flex-direction:column;background:var(--paper);border:var(--bd) solid var(--blue)}
.workshop__logo{display:flex;align-items:center;justify-content:flex-start;height:clamp(96px,9vw,120px);
   padding:0;margin:0 clamp(16px,2vw,26px);border-bottom:1px solid rgba(2,74,216,.28)}
.workshop__logo img{display:block;width:auto;height:auto;max-width:70%;max-height:44px;object-fit:contain}
.workshop__logo span{font-family:var(--mono);font-size:10px;color:var(--slate)}
.workshop__speaker-body{padding:clamp(16px,2vw,26px);flex:1;min-width:0}
.workshop__speaker-body h3,.workshop__speaker-body p{margin:0}
.workshop__speaker-body h3{font-family:var(--mono);font-size:16px;color:var(--blue)}
.workshop__speaker-body p{margin-top:9px;font-family:var(--text);font-size:13px;line-height:1.5;color:var(--slate)}
.workshop__speaker-body .workshop__speaker-role{font-family:var(--micro);font-weight:500;text-wrap:balance}
.workshop__copy{margin-top:14px}
.workshop__speaker-body .workshop__desc{margin:0 0 .85em;font-size:13.5px;line-height:1.6;
   color:var(--slate);text-wrap:pretty;hyphens:manual}
.workshop__speaker-body .workshop__desc:last-child{margin-bottom:0}
.workshop__desc strong{font-weight:500}
/* placeholder copy keeps the speaker bios' faded ink, not a fade of the grey */
.workshop__copy .tbc{color:var(--ink)}
@media (max-width:980px){.workshop__speakers{grid-template-columns:minmax(0,1fr)}}
/* The register list inverts: ink plate, white type, the same hairline rules
   in white, and the buttons flip to outline-on-blue with a white fill on hover. */
.workshop__list{margin-top:clamp(28px,4vw,48px);padding:0 clamp(16px,2.2vw,28px);
   background:var(--blue);border:var(--bd) solid var(--blue)}
.workshop__row{display:flex;align-items:center;justify-content:space-between;gap:20px;
   padding:18px 0;border-bottom:1px solid rgba(255,255,255,.3)}
.workshop__row:last-child{border-bottom:0}
.workshop__listhead{padding:clamp(22px,3vw,34px) 0 clamp(16px,2vw,22px);
   border-bottom:1px solid rgba(255,255,255,.3)}
.workshop__listhead h3{margin:0;font-size:clamp(22px,2.4vw,30px);letter-spacing:-.02em;color:#fff}
.workshop__listhead p{margin:8px 0 0;font-family:var(--text);font-size:14px;line-height:1.5;
   color:rgba(255,255,255,.72)}
.workshop__row h3,.workshop__row p{margin:0}
.workshop__row h3{font-family:var(--mono);font-size:17px;color:#fff}
.workshop__row p{margin-top:5px;font-family:var(--text);font-size:13px;color:rgba(255,255,255,.72)}
.workshop__register{flex:none;color:#fff;border:var(--bd) solid #fff;padding:11px 15px;
   font-family:var(--mono);font-size:10px;font-weight:500;letter-spacing:.08em;text-decoration:none}
.workshop__register:hover{background:#fff;color:var(--blue)}



/* ---------- the campus cards ----------
   The plate is the poster's: a heavy rule, the date up top in the machine voice,
   the name on the floor, and slabs breaking out through the border so the card
   has a silhouette rather than being a rectangle. The breakouts are xbox.js —
   the same object as the hero's REGISTER button — which is what keeps them in
   the same perspective as everything else instead of being a drop shadow's idea
   of depth. Hence no border on them and no background: the SVG is the shape. */
/* Three, explicitly, and no max-width. auto-fill picked the column count from
   whatever happened to fit, and the 1340px cap meant that on a wide window the
   grid stopped short of the plate above it — the cards have to land on the same
   two margins the About plate does, at every width, which they only do if the
   count is fixed and the grid is free to fill. */
.rcards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
   grid-auto-rows:1fr;align-items:stretch;
  gap:clamp(30px,3.6vw,56px) clamp(30px,3.4vw,52px);
  margin-top:clamp(34px,4.5vw,58px)}
/* Four groups, and the space between them is the card's own. The date, the
   campus with its room, the bill, and the two buttons are four answers to four
   questions; spacing them with fixed margins meant the leftover height piled up
   in whichever gap happened to carry the auto. space-between hands that height
   to all three gaps instead, so a short card breathes evenly rather than
   collapsing at the top and gaping at the bottom.

   The gap is the floor, not the spacing. Cards in a row stretch to the tallest,
   so the tallest one has no leftover to distribute and space-between alone
   would butt its four groups together; this keeps them apart whatever the row
   does to the height. */
.rcard{
  position:relative;display:flex;flex-direction:column;
  justify-content:space-between;gap:clamp(14px,1.6vw,22px);
  min-height:clamp(232px,20vw,286px);
  background:var(--paper);border:var(--bd) solid var(--blue);
  padding:clamp(18px,2vw,26px);
}
.rcard__top{display:flex;align-items:flex-start;justify-content:space-between;gap:14px}
.rcard__date{margin:0;font-family:var(--mono);font-weight:500;color:var(--blue);
  font-size:clamp(19px,1.95vw,28px);line-height:1;letter-spacing:-.02em;word-spacing:-.3em}
/* The corridor the breakouts live in: not optical, it is why a long name wraps
   before it reaches them instead of running underneath. */
.rcard__name{margin:0;font-family:var(--mono);font-weight:500;color:var(--blue);
  font-size:clamp(21px,2.25vw,32px);line-height:1.04;letter-spacing:-.035em;word-spacing:-.3em}
/* 3px, not 9. The venue is the second half of "where", so it belongs to the
   campus above it rather than floating between it and the bill. At 9px, with
   the venue's own 1.4 line-height on top, the pair read as two separate lines
   that happened to land near each other. */
.rcard__venue{margin:3px 0 0;font-family:var(--text);font-size:21px;
   line-height:1.4;color:var(--blue)}
.rcard__workshop{margin-top:18px;padding:14px;border:var(--bd) solid var(--blue);
   background:var(--paper)}
.rcard__workshop .rcard__go{margin-top:16px}
.rcard__exhibition{margin-top:13px}
.rcard__exhibition-name{margin:0;font-family:var(--mono);font-size:12px;
   line-height:1.25;font-weight:500;color:var(--ink)}
.rcard__exhibition-description{margin:4px 0 0;font-family:var(--text);font-size:12px;
   line-height:1.45;color:var(--slate)}

/* Who is actually standing in THIS room. The speaker grid further down is the
   whole tour's bill; without this line the card cannot say that Cornell and
   CMU are not the same evening. */
.rcard__bill{margin:0;font-family:var(--text);font-weight:400;font-size:12px;
  line-height:1.5;color:var(--slate)}
/* The rule between the chapter and the bill. Thinned to 40% so it separates
   without reading as a third item on a line that only has two, and spaced with
   padding rather than spaces in the string so the gap does not collapse or
   wrap away from the bar. */
.rcard__sep{padding:0 .5em;opacity:.4}


/* The card is a plate and the link sits on it. This was two extruded slabs
   breaking out through the right border for a while; off the card they read as
   debris floating in the gutter rather than as part of it, and the extrusion
   motif is already carrying the hero and the stats. One plate, one button. */
.rcard__go{
  display:inline-block;margin-top:clamp(16px,1.9vw,24px);align-self:flex-start;
  color:#fff;background:var(--blue);text-decoration:none;font-family:var(--mono);
  font-size:10.5px;letter-spacing:.0975em;word-spacing:-.2em;font-weight:500;
  padding:11px 17px;transition:background .18s ease,color .18s ease;
  border:var(--bd) solid var(--blue);
}
.rcard__actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:0}
.rcard__actions .rcard__go{margin-top:0}
.rcard__go--workshop{background:var(--paper);color:var(--blue)}
.rcard__go--workshop:hover{background:var(--blue);color:#fff}
.rcard__go:hover{background:var(--paper);color:var(--blue)}

/* ---------- registration ----------
   One page per stop, addressed by slug. Everything on it — the school, the
   chapter, the date, the caveat on the date — is read out of tour.js, so the
   twelve pages cannot disagree with the schedule they came from. */
/* The chapter is part of the campus's name, not a line of its own, so it stays
   in the h2 and steps back in colour instead of in size or weight — to the
   grey the swatch already gives everything subordinate, which is what the
   labels in the panel below use too. */
.reg__chapter{color:var(--slate)}

/* The workshop row. The other three rows are a label and a short value on one
   line; this one carries a sentence under its value, so it is the only row
   that has to align its label to the top rather than let flex centre it
   against two lines of blurb. The value column is capped at a measure so the
   sentence wraps into a block the width of a value rather than running the
   whole panel and leaving the label alone on the far left. */
.reg__line--ws{align-items:start}
.reg__line--ws > span{display:block;max-width:38ch}
.reg__wsnote{display:block;margin-top:5px;font-size:12px;line-height:1.5;
  color:var(--slate)}

.reg{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(26px,4vw,64px);margin-top:clamp(30px,4.5vw,54px);max-width:1180px;align-items:start}
.reg__meta{border:var(--bd) solid var(--blue);padding:clamp(18px,2.2vw,28px)}
/* The stop page is already two columns, so its form stays one. .field--wide is
   what the textarea, the checkbox and the button use to break out of a
   multi-column form; here there is only the one column to break out of. */
.rform{display:grid;grid-template-columns:minmax(0,1fr)}
.field--wide{grid-column:1/-1}
/* A two-column grid, not space-between. Pushed apart, every value ended on the
   panel's right edge and started wherever its own length put it — four ragged
   left edges down the column. The label column is a fixed width rather than
   auto because each row is its own grid: auto would size to that row's own
   label and the four would not line up. */
.reg__line{display:grid;grid-template-columns:clamp(78px,8.4vw,104px) minmax(0,1fr);
  gap:18px;padding:11px 0;
  font-family:var(--text);font-size:13.5px;color:var(--ink)}
.reg__line + .reg__line{border-top:1px solid rgba(2,74,216,.18)}
.reg__line b{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.03em;word-spacing:-.2em;color:var(--slate)}
.reg__line span{text-align:left}
.field{display:block;margin-bottom:clamp(14px,1.8vw,20px)}
.field > span{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.0525em;word-spacing:-.2em;
  font-weight:500;margin-bottom:7px;color:var(--slate)}
.field input,.field textarea,.field select{
  width:100%;font:inherit;font-size:14px;color:var(--ink);
  background:var(--paper);border:var(--bd) solid rgba(2,74,216,.45);border-radius:0;
  padding:11px 12px;appearance:none;
}
.field textarea{min-height:104px;resize:vertical}
.field input:focus,.field textarea:focus,.field select:focus{
  outline:none;box-shadow:inset 0 0 0 1px var(--blue)
}
.check{display:flex;gap:10px;align-items:flex-start;font-size:13px;line-height:1.55;color:var(--slate);
  margin-bottom:clamp(16px,2vw,22px)}
.check input{width:15px;height:15px;margin:2px 0 0;accent-color:var(--blue);flex:none}
.send{
  font-family:var(--mono);font-size:12px;letter-spacing:.0975em;word-spacing:-.2em;font-weight:500;
  color:#fff;background:var(--blue);border:var(--bd) solid var(--blue);border-radius:0;
  padding:14px 26px;cursor:pointer;transition:background .18s ease,color .18s ease;
}
.send:hover{background:var(--paper);color:var(--blue)}
/* Nothing is wired to a backend yet, so the form says so on submit instead of
   swallowing the entry and showing a success state for something that did not
   happen. Point the <form> at a real endpoint and delete this. */
.sent{margin:16px 0 0;font-size:13px;line-height:1.6;color:var(--ink);
  border-left:2px solid var(--blue);padding-left:12px}

/* ---------- gallery ----------
   One row filling the plate's margins, like the schedule. The tiles are the
   template: drop files into photos/ and point each one at an <img>. */
/* The grid's two numbers live here rather than in .grid, because the picker is
   sized off them: it is exactly one tile wide, and stays one tile wide when the
   column count steps down at a breakpoint. Hard-coding a width would be a third
   number to keep in step with the other two. */
.gal{--gal-cols:4;--gal-gap:clamp(12px,1.4vw,20px)}

/* The label and the current state share the line above the control, question on
   the left and answer on the right, both across the full strip. */
.galpick{margin:clamp(18px,2.4vw,28px) 0 clamp(14px,1.8vw,22px)}
.galpick__top{display:flex;align-items:baseline;justify-content:space-between;
  gap:18px;margin-bottom:9px}
.galpick__label{margin:0;font-family:var(--mono);font-size:10px;letter-spacing:.0525em;
  word-spacing:-.2em;font-weight:500;color:var(--slate)}
.galpick__wrap{position:relative;
  width:calc((100% - (var(--gal-cols) - 1) * var(--gal-gap)) / var(--gal-cols))}
/* Not a <select>: the platform's own list cannot be made to match the band's
   menu, and the two are the same object — a panel of campuses. This is a button
   plus .navdrop, so they stay one thing. */
.galpick__btn{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:16px;
  font-family:var(--text);font-weight:400;font-size:14px;
  color:var(--blue);background:var(--paper);text-align:left;
  border:var(--bd) solid var(--blue);border-radius:0;
  padding:12px 14px;cursor:pointer;
}
.galpick__btn svg{flex:none;transition:transform .2s cubic-bezier(.22,1,.36,1)}
.galpick__btn[aria-expanded="true"] svg{transform:rotate(180deg)}
/* Anchored to the trigger's left edge and free to grow past it: at one tile wide
   "Massachusetts College of Art and Design" would be three lines, and a campus
   list you cannot read at a glance is not a shortcut. */
.galdrop{left:0;right:auto;min-width:100%;width:max-content;max-width:min(440px,86vw)}

.gal__soon{margin:0;font-family:var(--mono);font-size:10.5px;letter-spacing:.0675em;
  word-spacing:-.2em;font-weight:500;color:var(--slate)}
.grid{display:grid;grid-template-columns:repeat(var(--gal-cols),minmax(0,1fr));
  gap:var(--gal-gap)}
.shot{position:relative;aspect-ratio:4/3;border:var(--bd) solid var(--blue);
  overflow:hidden;background:var(--paper)}
.shot img{width:100%;height:100%;object-fit:cover;display:block}
/* Over every plate, photo or not, until the tour has actually run. */
.shot__soon{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-weight:500;font-size:11.5px;letter-spacing:.0675em;
  word-spacing:-.2em;text-transform:uppercase;color:var(--blue)}
.shot img+.shot__soon{background:rgba(255,255,255,.72)}
/* background-IMAGE, not background: the shorthand would reset the paper colour
   .shot sets, leaving a hatch floating on the corridor rather than on a plate. */
.shot--empty{background-image:repeating-linear-gradient(135deg,
  rgba(2,74,216,.045) 0 9px,transparent 9px 18px)}

/* ---------- footer ----------
   The page inverts here. Everything above is blue on white; the footer is the
   same corridor seen the other way round — white hairlines on the ink — which
   is only possible because a slab's fill and the ground are the same colour, so
   the fills vanish and the outlines are all that is left. The frozen wireframe
   at the top of the sheet works by exactly that trick in reverse.

   It is also where the navigation lives on a phone: the band cannot hold four
   links at 430px, so the columns here are not decoration. */
.foot{
  position:relative;overflow:hidden;isolation:isolate;
  background:var(--blue);color:#fff;
  margin-top:clamp(64px,9vw,120px);
  min-height:clamp(430px,44vw,620px);display:flex;
}
/* Its own canvas and its own instance of the engine. Reusing the page's field
   is not an option: that one is fixed to the viewport and painted for a white
   ground, and this one has to sit in the flow and run inverted. */
.foot__field{
  position:absolute;inset:0;width:100%;height:100%;display:block;z-index:0;
  /* The corridor converges off the right edge, so its lines radiate back across
     the whole panel — including the half the navigation sits on. Tightening the
     cloud would not fix it (the near slabs are the long ones), so the canvas is
     faded out toward the left instead and the columns get clean ink to sit on.

     Eased, not linear, and running the full width. A straight alpha ramp reads
     as a cut on work this thin: a 1px hairline at 30% over saturated blue is
     already invisible, so with a linear gradient everything vanishes inside the
     top third of the ramp and the remaining two thirds do nothing. The stops
     below spend most of their length in the low alphas, where the eye can
     actually follow a line going. */
  --fade:linear-gradient(90deg,
    rgba(0,0,0,0) 0%,rgba(0,0,0,.02) 18%,rgba(0,0,0,.06) 30%,rgba(0,0,0,.13) 41%,
    rgba(0,0,0,.26) 52%,rgba(0,0,0,.45) 63%,rgba(0,0,0,.68) 75%,
    rgba(0,0,0,.89) 88%,#000 100%);
  -webkit-mask-image:var(--fade);
  mask-image:var(--fade);
}
.foot__in{
  position:relative;z-index:1;flex:1;display:grid;
  grid-template-rows:auto 1fr auto;
  /* the middle row is empty now, so it cannot be what separates the columns
     from the lockup when the panel stops being tall */
  row-gap:clamp(46px,6vw,84px);
  padding:clamp(34px,4.6vw,64px) var(--edge) clamp(28px,3.6vw,52px) var(--hero-x);
}
/* The panel itself arrives by scrolling — it is in flow. What animates is what
   is ON it: the columns and the lockup slide up as the corridor behind them
   comes into view, so the footer reads as a place you arrive at rather than a
   block that was always sitting below the fold. The lockup trails the columns,
   which is what makes it feel like one move settling rather than two things
   appearing at once. */
.foot__cols,.foot__mark{opacity:0;transform:translateY(28px);
  transition:opacity .8s cubic-bezier(.22,1,.36,1),transform .8s cubic-bezier(.22,1,.36,1)}
.foot.is-up .foot__cols,.foot.is-up .foot__mark{opacity:1;transform:none}
.foot.is-up .foot__mark{transition-delay:.12s}

.foot__cols{display:flex;gap:clamp(32px,6vw,96px);flex-wrap:wrap}
.foot__col{display:flex;flex-direction:column;gap:10px}
.foot__col h3{margin:0 0 5px;font-family:var(--mono);font-size:9.5px;letter-spacing:.09em;word-spacing:-.2em;
  font-weight:500;opacity:.5}
.foot__col a{font-family:var(--mono);font-size:11px;letter-spacing:.0375em;word-spacing:-.3em;font-weight:500;
  color:#fff;text-decoration:none;opacity:.86;transition:opacity .18s ease;width:max-content}
.foot__col a:hover{opacity:1;box-shadow:0 3px 0 -1px #fff}
/* The lockup sits on the floor of the panel, at the gutter, the way it does in
   the band — the two are the same mark at two sizes, not two lockups. */
.foot__mark{grid-row:3;height:clamp(42px,5.6vw,76px);width:auto;align-self:end;
  justify-self:start;display:block}

/* ---------- synthetic medium ----------
   A fallback for when the mono's Medium fails to load: without a real 500,
   every font-weight:500 resolves DOWN to Regular. This thickens the stems
   optically to sit between Regular and Bold. Scoped to the mono only, and in
   em so a 38px title and an 11px label thicken by the same proportion.

   pages.js adds .no-medium ONLY when no 500-weight IBM Plex Mono face is
   registered, so it stays dormant whenever the Google Fonts request succeeds. */
.no-medium h1,.no-medium h2,
.no-medium .rcard__name,.no-medium .spk__name,
.no-medium .rcard__date,
.no-medium .nav a,.no-medium .host,
.no-medium .rcard__go,
.no-medium .spk__photo span,
.no-medium .reg__line b,.no-medium .field > span,.no-medium .send,
.no-medium .gal__soon,.no-medium .galpick__label,
.no-medium .foot__col h3,.no-medium .foot__col a,
.no-medium .card__eyebrow,.no-medium .register{
  -webkit-text-stroke:.03em currentColor;
}



/* ---------- arrival ----------
   On index.html the sheet's own entrance is the scroll, so its contents cannot
   also be the scroll or the whole thing lands at once. On a sub-page there is
   nothing to swipe, so the blocks simply come in as they are reached. */
.rise{opacity:0;transform:translateY(22px);
  transition:opacity .7s cubic-bezier(.22,1,.36,1),transform .7s cubic-bezier(.22,1,.36,1)}
.blk.is-up .rise,.promo.is-up{opacity:1;transform:none}
.blk.is-up>.rise:nth-child(1){transition-delay:.08s}
.blk.is-up>.rise:nth-child(2){transition-delay:.16s}
.blk.is-up>.rise:nth-child(3){transition-delay:.24s}
.blk.is-up>.rise:nth-child(4){transition-delay:.32s}

@media (max-width:1180px){
  .spk{grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media (max-width:980px){
  .rcards{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:860px){
  .reg{grid-template-columns:minmax(0,1fr)}
  .spk{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:720px){
  :root{--bar-h:52px}
  /* narrower than the button's own offset allows, so a panel cannot hang off
     the right edge of a 360px screen */
  .navdrop{min-width:0;width:min(78vw,290px)}
  .brand img{height:36px}
  /* Four items will not fit beside the lockup at this width. The band keeps the
     one that is a call to action; the rest are in the footer, which is why the
     footer is on every page and not only on the index. */
  /* child combinator: the menu's own links are .nav a too, and hiding them
     here would empty the dropdown on exactly the width that needs it most */
  .nav > a:not(.host){display:none}
  .night__in,.page__in{padding-left:var(--edge);padding-right:var(--edge)}
  .foot__in{padding-left:var(--edge)}
  /* the corridor converges off the right edge, so at this width almost none of
     it is on screen — the panel has nothing to be tall for */
  .foot{min-height:0}
}
@media (max-width:820px){
  /* Side by side, the art column is down to a couple of hundred pixels and the
     laptop is a detail of a laptop. Stacked, it is a band across the top of the
     band — which is what the drawing was cropped for in the first place.

     The row is deep enough here for the drawing to reach across it at its own
     ratio. Sized any shallower, the laptop sits in the left third with the rest
     of the row empty, and the band reads as a picture that failed to load. */
  .promo{grid-template-columns:minmax(0,1fr)}
  /* the drawing is 1.82:1, so a full-width column needs about 55% of its own
     width in height to hold it without either cropping or leaving a band of
     empty blue under it */
  .promo__art{min-height:min(47vw,420px)}
  /* Centred here, unlike the side-by-side band. The reason it is flush left up
     there is that the export's left edge is a crop, and centring puts that cut
     in the middle of the band with blue on both sides of it. Stacked, the art
     is a full-width strip and the drawing no longer fills it, so left-flush
     reads as a drawing shoved into a corner instead of one carrying on past
     the page — and the crop lands on the strip's own edge either way once the
     width is this close to the drawing's. */
  .promo__art img{left:50%;transform:translate(-50%,-50%)}
}
@media (max-width:620px){
  .gal{--gal-cols:2}
  .rcards{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:520px){
  .spk{grid-template-columns:minmax(0,1fr)}
}
@media (prefers-reduced-motion:reduce){
  .rise{opacity:1;transform:none;transition:none}
  .foot__cols,.foot__mark{opacity:1;transform:none;transition:none}
}
