/* ------------------------------------------------------------------
   Shelf & Table - shared stylesheet
   One file for the whole site. No build step. Edit here, not per page.

   Palette and type are the collection page's: card room green, sand text,
   brass accent, Archivo / IBM Plex Sans / IBM Plex Mono. The shelf at
   /shelves/chris is generated by build-public.py and carries its own copy
   of these tokens, so the two must stay in step by hand. If you change a
   colour here, change it in the private collection source too.
------------------------------------------------------------------ */

:root {
  /* Card room green. A felt table is literally green, so the ground explains
     itself, and it puts distance between this and every other dark navy site. */
  --felt:      #13241D;
  --felt-deep: #081310;
  --felt-hi:   #1D3529;
  --sand:      #EFE8D8;
  --sand-dim:  #CEC5AE;
  --ink:       #07110D;
  --chip:      #5E9E8E;
  --brass:     #C98A3F;
  --sage:      #7A8F81;
  /* the table top, lit from the near edge. Kept as tokens rather than baked
     into the gradient so the whole palette can be swapped in one place. */
  --table-hi:  #1D3529;
  --table-lo:  #081310;

  /* the shelf board, lit along its front edge. Warm grey, not cool: a cool
     grey plank reads blue against green. */
  --board-hi:  #A8AC9C;
  --board-mid: #7E866F;
  --board-lo:  #4F5749;
  --board-off-hi: #48524A;   /* an empty shelf, unlit */
  --board-off-lo: #2F3830;

  /* everything that sits under or behind: shadows, sunken inputs */
  --well:      7, 17, 13;

  --line:      rgba(239,232,216,.15);
  --line-hi:   rgba(239,232,216,.28);
  /* dashed outlines have to carry further than a solid hairline to read */
  --line-dash: rgba(239,232,216,.38);

  /* spine fills. Mid-tones, all legible with ink text on top. */
  --spine-brass: #C9793F;
  --spine-chip:  #5B8FB9;
  --spine-moss:  #6E9E7F;
  --spine-gold:  #C9A227;
  --spine-plum:  #9E6E8E;

  --gap: 16px;
  --rail-h: 8px;
}

* { box-sizing: border-box; }

/* the gradient is viewport-fixed, so anything it does not paint has to be
   dark already or a long page flashes white past the fold */
html { -webkit-text-size-adjust: 100%; background: var(--felt-deep); }

body {
  margin: 0;
  padding: 0 0 64px;
  background:
    radial-gradient(120% 80% at 50% -10%, var(--felt-hi) 0%, var(--felt) 42%, var(--felt-deep) 100%);
  background-attachment: fixed;
  color: var(--sand);
  font-family: "IBM Plex Sans", system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

.wrap {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 clamp(20px, 3vw, 56px);
}

/* ---------- masthead ---------- */

.masthead { padding: 52px 0 26px; }
.masthead--sub { padding-bottom: 20px; }

/* The landing masthead is centred; every other page keeps its left edge,
   because those pages open with a breadcrumb bar and a centred title above a
   left-aligned crumb trail reads as a mistake. :not() rather than a second
   class so the markup does not have to know. */
.masthead:not(.masthead--sub) { text-align: center; }
.masthead:not(.masthead--sub) .standfirst { margin-left: auto; margin-right: auto; }

.wordmark {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(30px, 6vw, 66px);
  line-height: 1.02;
  letter-spacing: -0.035em;
  margin: 0;
  color: var(--sand);
  text-transform: uppercase;
}

.wordmark a { color: inherit; text-decoration: none; }

.wordmark .amp {
  font-family: "IBM Plex Mono", monospace;
  font-weight: 400;
  font-size: 0.46em;
  vertical-align: 0.28em;
  margin: 0 0.14em;
  color: var(--brass);
}

.standfirst {
  margin: 18px 0 0;
  max-width: 42ch;
  font-size: clamp(1rem, 2vw, 1.15rem);
  color: var(--sand-dim);
}

.standfirst--wide { max-width: 56ch; }

/* The lifted table blurb. Quieter and wider than the standfirst above it,
   so the masthead still reads as one line of billing followed by detail
   rather than two competing paragraphs. Empty until render.js fills it,
   and the :empty rule keeps it from opening a gap if it never does. */
.masthead-lede {
  margin: 14px 0 0;
  max-width: 62ch;
  font-size: 0.95rem;
  line-height: 1.65;
  color: var(--sage);
}

.masthead-lede:empty { display: none; }

.masthead:not(.masthead--sub) .masthead-lede { margin-left: auto; margin-right: auto; }

/* ---------- breadcrumb ---------- */

.topbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-top: 28px;
}

.crumbs {
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sage);
  margin: 0;
}

.crumbs a { color: var(--sand-dim); text-decoration: none; border-bottom: 1px solid var(--line); }
.crumbs a:hover { color: var(--sand); border-color: var(--sand-dim); }
.crumbs .sep { padding: 0 0.5rem; opacity: 0.45; }

.page-title {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(28px, 5.2vw, 58px);
  line-height: 1.04;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  margin: 22px 0 0;
}

/* ---------- section furniture ---------- */

.section { padding-bottom: clamp(40px, 6vw, 72px); }

/* Heading and gloss stack rather than sitting side by side. Beside the
   heading the gloss read as a stray fragment of the title; beneath it, it
   reads as the subtitle it is. */
.section-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  border-top: 1px solid var(--line);
  padding-top: 14px;
  margin-bottom: 28px;
}

.section-head h2 {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(20px, 3.2vw, 30px);
  letter-spacing: -0.025em;
  text-transform: uppercase;
  margin: 0;
}

.section-head h2 a { color: inherit; text-decoration: none; }
.section-head h2 a:hover { color: var(--brass); }

.section-head .gloss {
  font-family: "IBM Plex Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sage);
}

.prose { max-width: 62ch; color: var(--sand-dim); }
.prose h2 {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 600;
  font-size: 18px;
  letter-spacing: -0.01em;
  color: var(--sand);
  margin: 32px 0 8px;
}
.prose p { margin: 0 0 14px; }
.prose a { color: var(--sand); text-decoration-color: var(--line-hi); text-underline-offset: 3px; }
.prose a:hover { color: var(--brass); }

/* ---------- the split: tables left, shelves right ---------- */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}

@media (max-width: 900px) {
  .split { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}

.col { min-width: 0; }

/* ---------- a shelf: a board with a row of spines standing on it ---------- */

.shelves { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; }

.shelfunit {
  display: block;
  min-width: 0;
  text-decoration: none;
  color: inherit;
  transition: transform .18s ease;
}

.shelfunit:hover, .shelfunit:focus-visible { transform: translateY(-3px); }
.shelfunit:focus-visible { outline: 2px solid var(--brass); outline-offset: 6px; }

/* one board of a shelving unit: boxes on top, plank underneath */
.shelf-row + .shelf-row { margin-top: 14px; }

/* Taller than it needs to be for the drawing alone. The titles run up the
   spines, so the height of the board is the length of title that fits before
   it truncates. 112px gave about ten characters at a readable size, which
   turned most games into an ellipsis. */
.books {
  display: flex;
  min-width: 0;
  align-items: flex-end;
  gap: 3px;
  height: 152px;
  padding: 0 10px;
}

/* a box standing on its edge. Thick, because game boxes are. */
.box {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 1 auto;
  min-width: 6px;
  align-self: flex-end;
  height: var(--h, 80%);
  width: var(--w, 24px);
  background: var(--c, var(--brass));
  border-radius: 2px 2px 0 0;
  box-shadow: inset -3px 0 0 rgba(var(--well), .30),
              inset 0 0 0 1px rgba(var(--well), .22),
              inset 0 3px 0 rgba(255,255,255,.10);
}

/* the printed band across a box side, which is what separates a game box
   from a paperback at a glance */
.box::after {
  content: "";
  position: absolute;
  left: 0; right: 3px;
  top: 34%;
  height: 15%;
  background: rgba(255,255,255,.15);
  border-top: 1px solid rgba(var(--well), .18);
  border-bottom: 1px solid rgba(var(--well), .18);
}

.box.lean { transform-origin: bottom right; transform: rotate(7deg); }

/* The game's title, running up the spine the way it does on a real box.

   Light with a shadow rather than a colour picked per spine: the palette runs
   from tan to slate, and one text colour that survives both beats four that
   each only work on one. Sits above the printed band, which is a translucent
   white wash and does not swallow it.

   max-height with overflow hidden is what truncates: a long title fades out
   at the top of the spine instead of overflowing onto the board. Nothing is
   selectable or clickable here -- the shelf itself is the link. */
.spine-t {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  max-height: calc(100% - 12px);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-family: "IBM Plex Sans", system-ui, sans-serif;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, .96);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
  position: relative;
  z-index: 1;
  pointer-events: none;
  user-select: none;
}

/* boxes lying flat, stacked. The other half of every real game shelf. */
.stack {
  display: flex;
  flex-direction: column-reverse;
  gap: 2px;
  flex: 0 1 auto;
  min-width: 10px;
  width: var(--w, 56px);
  align-self: flex-end;
}

.stack i {
  display: block;
  height: 15px;
  border-radius: 2px;
  background: var(--c, var(--brass));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16),
              inset 0 -2px 0 rgba(var(--well), .30),
              inset 0 0 0 1px rgba(var(--well), .20);
}

.board {
  height: 9px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--board-hi) 0%, var(--board-mid) 55%, var(--board-lo) 100%);
  box-shadow: 0 8px 18px -10px rgba(0,0,0,.95);
}

/* the front edge of the board, so it reads as a plank rather than a line */
.board-edge {
  height: 4px;
  margin: 0 3px;
  border-radius: 0 0 2px 2px;
  background: rgba(var(--well), .55);
}

.plate {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding-top: 12px;
}

.plate-name {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 800;
  font-size: 19px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--sand);
}

.shelfunit:hover .plate-name { color: var(--brass); }

.plate-count {
  font-family: "IBM Plex Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sage);
}

/* An empty shelf: the board is there, and what stands on it is the offer.
   The dashed outline is the shelf's own empty space, so the invitation
   belongs inside it rather than floating underneath. Min-height rather
   than height, so the text sets the box instead of overflowing it. */
.shelfunit.is-open .books {
  min-height: 108px;
  border: 1px dashed var(--line-dash);
  border-bottom: 0;
  border-radius: 3px 3px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px 18px;
}

.shelfunit.is-open .board { background: linear-gradient(180deg, var(--board-off-hi) 0%, var(--board-off-lo) 100%); }
.shelfunit.is-open .plate-name { color: var(--sage); font-weight: 600; }
.shelfunit.is-open:hover .plate-name { color: var(--sand-dim); }

/* The invitation, set inside the empty shelf it is inviting someone onto.
   Same voice as .rail-note, which it replaced: mono, small, quiet. The inner
   <span> is the call to action; it cannot be an <a> because the whole unit
   already is one. */
.shelf-invite {
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  line-height: 1.7;
  color: var(--sage);
  margin: 0;
  max-width: 40ch;
  text-align: center;
}

.shelf-invite span { color: var(--sand-dim); border-bottom: 1px solid var(--line); }
.shelfunit:hover .shelf-invite span { color: var(--brass); border-color: var(--brass); }

.rail-note {
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  line-height: 1.7;
  color: var(--sage);
  margin: 18px 0 0;
}

.rail-note a { color: var(--sand-dim); text-decoration: none; border-bottom: 1px solid var(--line); }
.rail-note a:hover { color: var(--brass); border-color: var(--brass); }

/* ---------- a table: seen from above, four seats around it ---------- */

.tables { display: grid; grid-template-columns: minmax(0, 1fr); gap: 34px; }

.tableunit { display: grid; gap: 18px; }

.tabletop {
  position: relative;
  width: 100%;
  /* a table drawn much bigger than this stops reading as a table and starts
     reading as a background */
  max-width: 520px;
  aspect-ratio: 1;
  container-type: inline-size;
  /* rotating a full-size seat box grows its bounding rect past the square,
     which pushes the page sideways. Nothing visible sits out there, so clip. */
  overflow: hidden;
}

/* One seat box per person, each the full square, each rotated a quarter turn.
   The person is drawn at the top of that box, so rotating the box walks them
   round to the middle of the next edge, still facing the table.

   Not translate: a percentage translate resolves against the element's own
   size, not the container, so the seats would barely leave the middle. */
.seat {
  position: absolute;
  inset: 0;
  transform: rotate(var(--a));
  pointer-events: none;
}

/* shoulders behind, head on top: an overhead person in two shapes */
.seat::before,
.seat::after {
  content: "";
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  background: var(--sage);
}

/* shoulders sit behind, away from the table */
.seat::before {
  width: 20%;
  height: 9%;
  top: 5.5%;
  border-radius: 46% 46% 28% 28%;
  opacity: .38;
}

/* the head is nearer the table, and lighter, so it reads as a head */
.seat::after {
  width: 10.5%;
  aspect-ratio: 1;
  top: 8.5%;
  border-radius: 50%;
  background: var(--sand-dim);
}

.surface {
  position: absolute;
  inset: 22%;
  border-radius: 10px;
  background:
    radial-gradient(120% 100% at 50% 22%, var(--table-hi) 0%, var(--felt) 58%, var(--table-lo) 100%);
  box-shadow: inset 0 0 0 1px var(--line-hi),
              inset 0 0 40px rgba(0,0,0,.55),
              0 18px 34px -18px rgba(0,0,0,1);
  display: grid;
  place-items: center;
  padding: 9%;
  text-align: center;
}

/* the accent edge of the table top */
.surface::before {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 13px;
  border: 3px solid var(--accent, var(--brass));
  opacity: .85;
}

.surface-inner { display: grid; gap: 3cqi; justify-items: center; }

.tablename {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(12px, 4.3cqi, 19px);
  line-height: 1.06;
  letter-spacing: -0.025em;
  text-transform: uppercase;
  margin: 0;
  color: var(--sand);
  max-width: 11ch;
}

.tablewhere {
  font-family: "IBM Plex Mono", monospace;
  font-size: clamp(8px, 2.1cqi, 10px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent, var(--brass));
}

.tablenext {
  margin: 0;
  display: grid;
  gap: 1cqi;
  border-top: 1px solid var(--line);
  padding-top: 3cqi;
  max-width: 22ch;
}

.tablenext dt {
  font-family: "IBM Plex Mono", monospace;
  font-size: clamp(7px, 1.9cqi, 9px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sage);
}

.tablenext dd {
  margin: 0;
  font-family: "IBM Plex Mono", monospace;
  font-size: clamp(9px, 2.5cqi, 12px);
  line-height: 1.35;
  color: var(--sand);
}

/* an empty table: nobody seated, nothing scheduled */
.tableunit.is-open .surface {
  background: none;
  box-shadow: inset 0 0 0 1px var(--line);
}

.tableunit.is-open .surface::before { border-style: dashed; opacity: .6; }
.tableunit.is-open .tablename { color: var(--sand-dim); }

/* an empty table is an invitation, not the main event */
.tableunit.is-open .tabletop { max-width: 380px; }

.tableunit.is-open .seat::before,
.tableunit.is-open .seat::after {
  background: none;
  border: 1px dashed var(--line-dash);
}

.tableblurb { margin: 0; color: var(--sand-dim); }

/* ---------- shared card furniture, used under a table ---------- */

.panel .table-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}

.panel p { margin: 0; color: var(--sand-dim); max-width: 54ch; }

.panel {
  background: var(--felt);
  border-radius: 4px;
  border-left: 8px solid var(--accent, var(--brass));
  padding: clamp(18px, 2.6vw, 26px);
  box-shadow: inset 0 0 0 1px var(--line),
              0 10px 22px -14px rgba(0,0,0,.9);
  display: grid;
  gap: 16px;
}

/* ---------- controls: pills, as on the collection page ---------- */

.actions { display: flex; flex-wrap: wrap; gap: 8px; }

.btn {
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 9px 15px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--sand-dim);
  background: transparent;
  text-decoration: none;
  cursor: pointer;
  transition: .15s;
}

.btn:hover, .btn:focus-visible { border-color: var(--sand-dim); color: var(--sand); }
.btn:focus-visible { outline: 2px solid var(--brass); outline-offset: 3px; }

.btn-solid {
  background: var(--accent, var(--brass));
  border-color: var(--accent, var(--brass));
  color: var(--ink);
  font-weight: 600;
}

.btn-solid:hover, .btn-solid:focus-visible {
  background: var(--sand); border-color: var(--sand); color: var(--ink);
}

/* ---------- signup ---------- */

.signup { display: grid; gap: 10px; }

.signup-label {
  font-family: "IBM Plex Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sage);
}

.signup-row { display: flex; gap: 8px; flex-wrap: wrap; }

.signup input {
  flex: 1 1 200px;
  min-width: 0;
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 15px;
  padding: 10px 14px;
  border-radius: 999px;
  background: rgba(var(--well), .55);
  border: 1px solid var(--line);
  color: var(--sand);
}

.signup input::placeholder { color: var(--sage); }
.signup input:focus-visible { outline: none; border-color: var(--brass); }

.signup-msg {
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  min-height: 1.2em;
  color: var(--brass);
}

.signup-fine {
  font-family: "IBM Plex Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.04em;
  line-height: 1.6;
  color: var(--sage);
  margin: 0;
  max-width: 52ch;
}

.signup-fine a { color: var(--sage); }
.signup-fine a:hover { color: var(--sand-dim); }

/* the meetup line, for where it sits outside the table rather than on it */
.next {
  display: none;
  gap: 14px;
  align-items: baseline;
  flex-wrap: wrap;
  font-family: "IBM Plex Mono", monospace;
  font-size: 13px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 12px 0;
  margin: 0;
}

.next dt {
  color: var(--accent, var(--brass));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-size: 10px;
  margin: 0;
}

.next dd { margin: 0; color: var(--sand); }

/* The next meetup's Facebook event, sitting under the meetup line inside the
   table. Sized in cqi like everything else on the table top, so it shrinks
   with the table instead of bursting out of it.

   Rendered twice, exactly as the meetup line above it is: .tableevent inside
   the table for normal widths, .nextevent outside for narrow ones. Below
   430px the table is too small to hold its own text, so both drop out
   together and reappear underneath. A link that outlived its meetup line
   would be pointing at a date nobody can see. */
.tableevent {
  font-family: "IBM Plex Mono", monospace;
  font-size: clamp(8px, 2.1cqi, 10px);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent, var(--brass));
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 1px;
  line-height: 1.2;
}

.tableevent:hover, .tableevent:focus-visible { color: var(--sand); }

.nextevent {
  display: none;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent, var(--brass));
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  align-self: flex-start;
  margin-top: 12px;
}

.nextevent:hover, .nextevent:focus-visible { color: var(--sand); }

/* ---------- facts strip ---------- */

.facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin: 0 0 30px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.facts > div {
  padding: 14px 24px 14px 0;
  margin-right: 24px;
  border-right: 1px solid var(--line);
  flex: 1 1 auto;
}

.facts > div:last-child { border-right: 0; margin-right: 0; }

.facts dt {
  font-family: "IBM Plex Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sage);
  margin: 0 0 4px;
  order: 2;
}

.facts dd {
  margin: 0 0 2px;
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 800;
  font-size: 22px;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.facts > div { display: flex; flex-direction: column; }

/* ---------- schedule ---------- */

.schedule { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }

.schedule li {
  display: flex;
  gap: 18px;
  align-items: baseline;
  flex-wrap: wrap;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}

.schedule .when {
  font-family: "IBM Plex Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brass);
  flex: 0 0 14rem;
}

.schedule .what { font-weight: 600; color: var(--sand); }
.schedule .note { color: var(--sage); font-size: 14px; width: 100%; }
.schedule .actions { width: 100%; margin-top: 10px; }
.schedule .btn { padding: 7px 12px; font-size: 10px; }

/* the generated Facebook post, on the cross-post panel */
.posttext {
  width: 100%;
  font-family: "IBM Plex Mono", monospace;
  font-size: 12px;
  line-height: 1.6;
  color: var(--sand);
  background: rgba(var(--well), .55);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 14px;
  resize: vertical;
  margin: 0 0 16px;
}

.posttext:focus-visible { outline: none; border-color: var(--brass); }

/* ---------- callout ---------- */

.callout {
  border: 1px dashed var(--line-dash);
  border-radius: 4px;
  padding: clamp(20px, 3vw, 28px);
  max-width: 62ch;
}

.callout h3 {
  font-family: "Archivo", system-ui, sans-serif;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  font-size: 17px;
  margin: 0 0 10px;
}

.callout p { margin: 0 0 16px; color: var(--sand-dim); }

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

footer {
  border-top: 1px solid var(--line);
  padding: 20px 0 0;
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-family: "IBM Plex Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  color: var(--sage);
  text-transform: uppercase;
}

footer a { color: var(--sand-dim); text-decoration: none; border-bottom: 1px solid var(--line); }
footer a:hover { color: var(--sand); border-color: var(--sand-dim); }

footer nav { display: flex; gap: 16px; flex-wrap: wrap; }

/* ---------- narrow screens ---------- */

@media (max-width: 700px) {
  /* a shelf gets shorter, not narrower: the boxes already flex to fit */
  .books { height: 88px; padding: 0 6px; }
  .stack i { height: 12px; }
  .schedule .when { flex: 1 1 100%; }
  .facts > div { border-right: 0; margin-right: 0; flex-basis: 45%; }
}

/* Below this the table drawing is too small to hold its own text, so the
   meetup details come out of the middle and sit underneath it instead. */
@media (max-width: 430px) {
  .tabletop { max-width: 270px; margin: 0 auto; }
  .surface { padding: 9%; }
  .tablenext { display: none; }
  .tableevent { display: none; }
  .tableunit .next { display: flex; }
  .tableunit .nextevent { display: inline-block; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
