/* public_html/assets/css/site.css */

:root{
  --felt:#1B3A2F;
  --felt-deep:#12241D;
  --felt-lift:#24503F;
  --bone:#F2EBDC;
  --bone-edge:#D9CFBB;
  --pip:#16110E;
  --clay:#B4522E;
  --chalk:#9DB0A5;

  /* The one opaque surface on the page. Everything else on the felt is
     translucent and lets the moving glow through; an opened card has to
     stop it, or the thing you are trying to read has weather behind it. */
  --panel:#14302A;

  /*
     The two text colours, as bare channels, so a rule can ask for them at
     an alpha. Every border, hairline and hover wash in this file is one of
     these -- which is what lets the whole palette invert inside a card by
     rebinding four properties rather than editing two hundred rules. See
     the CARDS section at the foot of the file.
  */
  --bone-rgb:242 235 220;
  --chalk-rgb:157 176 165;

  /* The card surface: a bone sheet laid on the felt, the same two colours
     the hero card and the date chips have always used. */
  --card:#F2EBDC;
  --card-edge:#D9CFBB;
  --card-sunk:#E8DFCA;
  --ink:#16110E;
  --ink-soft:#5B5346;
  --ink-rgb:22 17 14;
  --ink-soft-rgb:91 83 70;

  /* Clay is clay on either surface, so anything sitting on it keeps light
     text whichever side of the boundary the rule is written on. */
  --on-clay:#F2EBDC;

  /* How hard a card lands on the table. A token because the light theme
     needs the same depth from a much softer, warmer shadow -- black at a
     third over pale paper reads as dirt rather than as height. */
  --card-shadow:0 14px 32px rgb(0 0 0 / .34);
  --card-shadow-sm:0 8px 20px rgb(0 0 0 / .26);
  --card-shadow-pill:0 6px 16px rgb(0 0 0 / .28);

  /* The drifting light. Channels only, so each layer keeps its own alphas
     and the two themes differ by three lines rather than by five gradients. */
  --glow-felt-rgb:36 80 63;
  --glow-warm-rgb:200 96 54;
  --glow-warm-deep-rgb:180 82 46;

  /*
     The rest of what a theme is. These seven were one-off overrides in the
     light block, which meant the light theme was a token list PLUS seven
     rules -- and following prefers-color-scheme means stating the whole
     theme twice, once per trigger. Every one of them is a token now, so the
     duplicated part at the foot of this file is a token list and nothing
     else. A theme is a set of values, not a set of rules.
  */
  --grain-opacity:.16;
  --hero-border:0 solid transparent;
  --hero-shadow:0 18px 28px rgba(0,0,0,.45);
  --home-shadow:0 24px 60px rgba(0,0,0,.42);
  --chip-shadow:0 3px 7px rgba(0,0,0,.3);
  --tile-shadow:0 18px 28px rgba(0,0,0,.45);
  --avatar-shadow:0 2px 6px rgba(0,0,0,.28);
  --bar-shadow:0 -6px 20px rgb(0 0 0 / .3);

  /*
     The domino, as three of its own tokens rather than as --bone/--pip.
     A tile on the felt is a bone face with dark pips; the same tile on a
     bone card is the other half of the domino, dark face and bone pips.
     Written out rather than as var(--bone), because a custom property whose
     value is another var() is resolved where it is DECLARED -- here, on
     :root -- so it would not follow the rebind inside a card.
  */
  --tile-face:#F2EBDC;
  --tile-edge:#D9CFBB;
  --tile-pip:#16110E;

  /* Column width. Pages that need more room raise it on <body>. */
  --measure:35rem;

  --font-display:"Alfa Slab One", Georgia, serif;
  --font-body:"Karla", system-ui, -apple-system, "Segoe UI", sans-serif;
}

*{box-sizing:border-box;margin:0;padding:0}

html{height:100%}

body{
  min-height:100%;
  display:flex;
  flex-direction:column;
  align-items:center;
  padding:2.5rem 1.25rem 1.5rem;
  background-color:var(--felt);
  background-image:radial-gradient(ellipse at 50% 34%, var(--felt-lift) 0%, var(--felt) 45%, var(--felt-deep) 100%);
  background-attachment:fixed;
  color:var(--bone);
  font-family:var(--font-body);
  font-size:17px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
}

/* Felt grain. Above the gradient, below the content. */
body::before{
  content:"";
  position:fixed;
  /* Oversized because it drifts: pinned at inset:0 the translate would
     walk a bare edge into view. */
  inset:-4%;
  pointer-events:none;
  opacity:var(--grain-opacity);
  mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23n)'/%3E%3C/svg%3E");
}

.page{
  position:relative;
  z-index:1;
  width:100%;
  max-width:var(--measure);
  margin:auto auto 0;
}

/* ---------------------------------------------------------------- domino */

.tile{
  display:block;
  width:min(15rem, 62vw);
  height:auto;
  margin:0 auto;
  filter:drop-shadow(var(--tile-shadow));
  animation:deal 800ms cubic-bezier(.2,.8,.25,1) both;
}
.tile-face{fill:var(--tile-face)}
.tile-edge{fill:var(--tile-edge)}
.tile-mark{fill:var(--tile-pip)}

@keyframes deal{
  from{opacity:0;transform:translateY(-1.75rem) rotate(-7deg) scale(.94)}
  to{opacity:1;transform:none}
}

/* ------------------------------------------------------------------ home */

.card{text-align:center}

.wordmark{
  font-family:var(--font-display);
  font-weight:400;
  font-size:clamp(3.4rem, 15vw, 5.75rem);
  line-height:.95;
  letter-spacing:-.01em;
  margin-top:2rem;
  text-shadow:0 3px 0 rgba(0,0,0,.28);
}
.wordmark .x{color:var(--clay)}

.status{
  display:flex;
  align-items:center;
  gap:1rem;
  margin:1.5rem 0 1.75rem;
  color:var(--chalk);
  font-size:.8rem;
  font-weight:600;
  letter-spacing:.22em;
  text-transform:uppercase;
}
.status::before,
.status::after{
  content:"";
  flex:1;
  height:1px;
  background:linear-gradient(to right, transparent, rgb(var(--chalk-rgb) / .5), transparent);
}

.lede{
  font-size:clamp(1.06rem, 3.4vw, 1.24rem);
  margin-bottom:1rem;
  text-wrap:balance;
}

.sub{
  color:var(--chalk);
  text-wrap:balance;
}

.note{
  margin-top:2.75rem;
  padding-top:1.75rem;
  border-top:1px solid rgb(var(--chalk-rgb) / .22);
  font-size:.95rem;
  color:var(--chalk);
  text-wrap:balance;
}

/* ------------------------------------------------------------------ links */

a{
  color:var(--bone);
  text-decoration-color:var(--clay);
  text-decoration-thickness:2px;
  text-underline-offset:4px;
}
a:hover{color:var(--clay)}
a:focus-visible{
  outline:2px solid var(--clay);
  outline-offset:3px;
  border-radius:2px;
}

/* ------------------------------------------------------ inline separators */

/*
   `.sep` is the middot between facts on a line: "$5 per person . Venue
   website . Venue on Facebook". Seven blocks style one, and every one of
   them is built by joining the parts with the span and NO whitespace --
   implode('<span class="sep">.</span>', $facts) in events.php, and the same
   shape by hand elsewhere.

   No whitespace means no soft wrap opportunity. Several of those lines also
   set white-space:nowrap on their links so a label stays whole, which
   removes the breaks inside them too -- so the whole run became one
   unbreakable string that simply ran off the side of the card on a phone.
   The comment on .event-facts a has always claimed "the line breaks between
   them"; this is the line that makes that true.

   A zero width space, because the alternatives are worse. Putting real
   whitespace in the PHP changes 32 call sites and leaves a visible gap the
   padding is already providing. Making each line a flex container fixes the
   wrapping but re-centres and re-spaces seven blocks that are currently
   plain text flow. This is one declaration and it cannot alter any layout
   that was not already overflowing.
*/
.sep::after{
  content:"\200B";
}

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

.site-footer{
  position:relative;
  z-index:1;
  width:100%;
  max-width:var(--measure);
  margin:auto auto 0;
  padding-top:3.5rem;
  text-align:center;
  /* Full chalk, not a faded version of it. The disclosure down here is
     required text and has to be readable; the warm light in the background
     had taken it to about 3:1, which it should not have been at even
     before. Quiet comes from the size, not from washing it out. */
  color:var(--chalk);
  font-size:.8rem;
  line-height:1.65;
}

.disclosure{
  max-width:30rem;
  margin:0 auto 1.1rem;
  text-wrap:balance;
}

.legal{letter-spacing:.02em}
.legal .sep{padding:0 .5rem;opacity:.5}

.mark{
  display:inline-flex;
  margin-top:1.5rem;
  padding:.4rem;
  border-radius:4px;
  opacity:.28;
  transition:opacity 180ms ease;
}
.mark:hover,
.mark:focus-visible{opacity:.9}

.mark-tile{
  display:block;
  width:26px;
  height:13px;
  animation:none;
  filter:none;
}
.mark-tile .tile-face{fill:var(--tile-face)}
.mark-tile .tile-edge{fill:var(--tile-face)}
.mark-tile .tile-mark{fill:var(--tile-pip)}

.version{
  margin-top:.75rem;
  font-size:.7rem;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:rgb(var(--chalk-rgb) / .4);
}

/* ------------------------------------------------------------------ bare */

body.bare .site-footer{display:none}

/* ------------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce){
  .tile{animation:none}
  .mark{transition:none}
}

/* ================================================================= events */
/*
   Two shapes carry the whole calendar. The next game is a bone card, laid on
   the felt the way a tile is; everything after it is a row with a small bone
   date chip on the left. The chip and the card share the tile's colours on
   purpose, so the page reads as one table rather than as a list widget that
   happens to sit on a green background.
*/

body.wide{--measure:42rem}

/*
   The form and legal pages fill the window too, but what is inside them
   keeps a measure.

   This is the one place the width is not simply taken. A 1900px text input
   is not an improvement on a 600px one -- it is further for the eye to
   travel back -- and a 1900px line of a privacy policy is unreadable in a
   way a narrow column never is. So the CARD spans the window and the fields
   and the prose sit in a column inside it. .legal-doc already had its own
   38rem for exactly this reason; this is the same idea applied to the form.

   :not(.roomy) is what picks those pages out, and it is the same line the
   rest of the file already draws: every page is `wide`, and `roomy` is the
   one that marks a list or a grid. /account and backstage are both, and
   they are lists -- centring their one form in an otherwise full width
   column looked like a mistake, which is how this scope got found.
*/
@media (min-width: 62rem){
  body.wide{--measure:100%}

  /* The card spans; what is inside it sits in a column, the same way the
     legal card does. Capping the form itself instead left a 46rem card
     stranded under a full width header. */
  body.wide:not(.roomy) form.form > *,
  body.wide:not(.roomy) .form-notice,
  body.wide:not(.roomy) .sheet > .form-hint,
  body.wide:not(.roomy) .sheet > .desk-line,
  body.wide:not(.roomy) .sheet > .desk-note{
    max-width:46rem;
    margin-left:auto;
    margin-right:auto;
  }

  /*
     The legal card is the exception to the exception. Its 38rem lived on the
     card itself, so capping the page left a narrow card stranded under a
     full width header. The card takes the width like every other card and
     the 38rem moves inward onto the prose, which is where the measure was
     always actually for.
  */
  body.wide:not(.roomy) .legal-doc{max-width:none}
  body.wide:not(.roomy) .legal-doc > *{
    max-width:38rem;
    margin-left:auto;
    margin-right:auto;
  }

  /* One long centred line under a title is the other thing a full width
     card can produce. */
  .sheet-sub{
    max-width:52rem;
    margin-left:auto;
    margin-right:auto;
  }
}


/* ================================================================ desktop */
/*
   Using the width, where there is something to do with it.

   The column is 42rem because that is a comfortable measure for reading,
   and on a phone or a laptop that is the whole screen anyway. On a big
   monitor it left most of the glass empty -- so pages that are *lists* or
   *grids* get to spread out, and pages that are *forms* deliberately do
   not. A 1200px wide text input is not an improvement on a 600px one; it
   is just further for the eye to travel back.

   That is what the roomy class marks: /events, /calendar, backstage and
   /account carry it, and /submit, /join, /report and the legal pages do
   not, even though all of them are "wide".
*/

@media (min-width: 62rem){
  /*
     The whole window, not a wider column.

     This used to climb in three steps -- 54rem, then 64rem, then 84rem past
     100rem of window -- which on a 1920 screen still left about 290px of
     empty felt down each side. A list and a month grid are the two things
     here that get better the more room they have, so they take the lot and
     body's own 1.25rem padding is the only gutter.

     Note this is a percentage, so `.page` and `.site-footer` resolve it
     against the window rather than against a font size. There is no upper
     bound on purpose: the card grid below is auto-fill, so a wider window
     buys another column rather than wider cards.
  */
  body.roomy{--measure:100%}

  /*
     Columns of cards. The scroll on a thirteen week run halves, and a
     collapsed card has no business being 800px wide.

     auto-fill against a minimum, not a column count. Counting columns means
     picking a number per breakpoint and getting it wrong: three across at
     1920 gave 340px cards with the title broken over three lines, which is
     worse than two. A 26rem floor lets the browser fit whatever actually
     fits, and the only thing left to tune is how wide the page gets.
  */
  body.roomy .event-list.cards,
  body.home .event-list.cards{
    display:grid;
    grid-template-columns:repeat(auto-fill, minmax(26rem, 1fr));
    gap:.9rem;
    /* Without this a short card stretches to match a tall one beside it
       and the pair read as one block. */
    align-items:start;
  }

  /* Separator lines are for a single column. Side by side they draw a
     grid of stray rules, so each item becomes an actual card instead. */
  body.roomy .event-list.cards .event-item,
  body.roomy .event-list.cards .event-item:last-child,
  body.home .event-list.cards .event-item,
  body.home .event-list.cards .event-item:last-child{
    border:1px solid rgb(var(--chalk-rgb) / .16);
    border-radius:10px;
    padding:0 .7rem;
  }
  body.roomy .event-list.cards .event-card[open],
  body.home .event-list.cards .event-card[open]{
    /* It already has its own border; the item's would double it. */
    margin:-1px;
  }
  body.roomy .event-list.cards .event-item:has(> .event-card[open]),
  body.home .event-list.cards .event-item:has(> .event-card[open]){
    border-color:transparent;
  }
}

/* The day cells get taller as they get wider, so a cell stays roughly
   square rather than becoming a letterbox with one game in it. */
@media (min-width: 82rem){
  .cal-day{min-height:7rem}
}

@media (min-width: 100rem){
  .cal-day{min-height:8.5rem}
}

/*
   The front page fills the window like the rest, but not by stretching the
   parts of it that are a centred composition.

   The masthead and the next game are one object -- a tile laid on a table
   with a card under it -- and an object does not get better at 1900px, it
   just gets a 1900px line of body text. So the page takes the whole width
   and those two keep a measure inside it; what spreads is the run of dates
   below, which is a list and behaves like every other list on the site.
*/
@media (min-width: 62rem){
  body.home{--measure:100%}

  body.home .event-hero,
  body.home .status,
  body.home .lede,
  body.home .sub,
  body.home .note{
    max-width:46rem;
    margin-left:auto;
    margin-right:auto;
  }
}

/* Pages that show a list stop being a centred single card. */
body.has-events .card{text-align:center}
body.has-events .status{margin-bottom:1.5rem}

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

.event-hero{
  position:relative;
  text-align:center;
  padding:2rem 1.5rem 1.75rem;
  border-radius:14px;
  background:var(--bone);
  color:var(--pip);
  border:var(--hero-border);
  box-shadow:var(--hero-shadow);
  animation:deal 800ms cubic-bezier(.2,.8,.25,1) both;
}

/* The tile's centre bar, echoed as the card's top edge. */
.event-hero::before{
  content:"";
  position:absolute;
  left:50%;
  top:0;
  width:64px;
  height:5px;
  transform:translateX(-50%);
  border-radius:0 0 3px 3px;
  background:var(--clay);
}

.event-flag{
  display:inline-block;
  margin-bottom:.85rem;
  color:var(--clay);
  font-size:.72rem;
  font-weight:600;
  letter-spacing:.2em;
  text-transform:uppercase;
}
.event-flag.is-live{
  padding:.15rem .6rem;
  border-radius:999px;
  background:var(--clay);
  color:var(--bone);
}
.event-flag.is-off{color:rgba(22,17,14,.55)}

.event-title{
  font-family:var(--font-display);
  font-weight:400;
  font-size:clamp(1.5rem, 5.5vw, 2rem);
  line-height:1.15;
  text-wrap:balance;
}

.event-when{
  margin-top:.7rem;
  font-size:1.05rem;
  font-weight:600;
}
.event-when .sep{padding:0 .5rem;opacity:.4}

.event-where{
  margin-top:.2rem;
  color:rgba(22,17,14,.7);
  text-wrap:balance;
}

.event-summary{
  margin-top:1rem;
  text-wrap:balance;
}

.event-details{
  margin-top:.75rem;
  font-size:.95rem;
  color:rgba(22,17,14,.72);
  text-align:left;
  text-wrap:pretty;
}

.event-note{
  margin-top:.75rem;
  padding:.5rem .75rem;
  border-radius:6px;
  background:rgba(180,82,46,.12);
  font-size:.92rem;
  color:rgba(22,17,14,.78);
  text-wrap:balance;
}

.event-facts{
  margin-top:1.25rem;
  padding-top:1rem;
  border-top:1px solid rgba(22,17,14,.14);
  font-size:.9rem;
  color:rgba(22,17,14,.7);
}
.event-facts .sep{padding:0 .55rem;opacity:.4}

/*
   Each fact stays whole and the line breaks between them. Without the nowrap
   a narrow card splits "Venue on Facebook" across two lines and the label
   stops reading as one thing.

   The other four declarations are the floor under that. white-space:nowrap
   suppresses line breaking outright, so overflow-wrap cannot reach inside it
   -- meaning a single fact wider than the column had no way to fit and simply
   ran out of the card. Nothing on the site is close to that today (the widest
   is about 130px in a 280px column), but a long contact name in that slot
   would do it. As an inline-block it can be told a maximum, and what will not
   fit ends in an ellipsis inside the card rather than off the side of it.
   vertical-align because an inline-block that clips its overflow sits on the
   line by its bottom edge instead of its baseline.
*/
.event-facts a{
  white-space:nowrap;
  display:inline-block;
  max-width:100%;
  overflow:hidden;
  text-overflow:ellipsis;
  vertical-align:bottom;
}

/* Links inside the bone card run dark; the global rule is for the felt. */
.event-hero a{color:var(--pip)}
.event-hero a:hover{color:var(--clay)}

.event-hero.is-cancelled{background:var(--bone-edge)}
.event-hero.is-cancelled .event-title{
  text-decoration:line-through;
  text-decoration-color:var(--clay);
  text-decoration-thickness:2px;
}

/* ----------------------------------------------------------------- rows */

.event-then{margin-top:2.25rem}

.event-then-head,
.event-month{
  display:flex;
  align-items:center;
  gap:.9rem;
  margin-bottom:.85rem;
  color:var(--chalk);
  font-family:var(--font-body);
  font-size:.72rem;
  font-weight:600;
  letter-spacing:.2em;
  text-transform:uppercase;
}
.event-then-head::after,
.event-month::after{
  content:"";
  flex:1;
  height:1px;
  background:linear-gradient(to right, rgb(var(--chalk-rgb) / .4), transparent);
}
.event-month{margin-top:2rem}
.event-month:first-of-type{margin-top:0}

.event-list{
  list-style:none;
  text-align:left;
}

/* The card is a <details>; the row is its <summary>. The rule that used to
   sit on .event-row moves out to the item, or a card would draw a line
   through the middle of itself when opened. */
.event-item{border-top:1px solid rgb(var(--chalk-rgb) / .18)}
.event-item:last-child{border-bottom:1px solid rgb(var(--chalk-rgb) / .18)}

.event-row{
  display:grid;
  grid-template-columns:auto 1fr auto;
  align-items:start;
  gap:.9rem;
  padding:.85rem 0;
  cursor:pointer;
  /* Both are needed: the second is what actually removes the triangle in
     WebKit, and the first in everything else. */
  list-style:none;
}
.event-row::-webkit-details-marker{display:none}
.event-row:focus-visible{
  outline:2px solid var(--clay);
  outline-offset:-2px;
  border-radius:6px;
}

/* Backstage still uses a plain <li class="event-row"> for its own lists,
   which never expand, so those keep the pointer off. */
li.event-row{cursor:default;border-top:1px solid rgb(var(--chalk-rgb) / .18)}
li.event-row:last-child{border-bottom:1px solid rgb(var(--chalk-rgb) / .18)}

/* The date chip is a domino half: bone face, dark marks. */
.event-date{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  width:3.1rem;
  padding:.35rem .25rem .4rem;
  border-radius:7px;
  background:var(--tile-face);
  color:var(--tile-pip);
  box-shadow:var(--chip-shadow);
  line-height:1;
}
.event-date .dow{
  font-size:.62rem;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
  opacity:.6;
}
.event-date .dnum{
  margin-top:.12rem;
  font-family:var(--font-display);
  font-size:1.25rem;
}
.event-date .mon{
  margin-top:.1rem;
  font-size:.6rem;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
  opacity:.6;
}

.event-row-main{
  display:flex;
  flex-direction:column;
  gap:.12rem;
  min-width:0;
}

.event-row-title{
  font-weight:600;
  line-height:1.35;
  text-wrap:balance;
}

.event-row-kind{
  margin-left:.5rem;
  padding:.05rem .4rem;
  border:1px solid rgba(180,82,46,.6);
  border-radius:3px;
  color:var(--clay);
  font-size:.62rem;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
  white-space:nowrap;
  vertical-align:.12em;
}

.event-row-where,
.event-row-summary{
  color:var(--chalk);
  font-size:.9rem;
  line-height:1.45;
}
.event-row-summary{opacity:.75}

.event-row-off{
  margin-top:.2rem;
  color:var(--clay);
  font-size:.82rem;
  font-weight:600;
}

.event-row-time{
  display:flex;
  align-items:center;
  gap:.5rem;
  padding-top:.1rem;
  color:var(--chalk);
  font-size:.9rem;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
}

/* The only thing on the row that says it opens. Kept quiet until the row is
   hovered, so a list at rest still reads as a list rather than as a stack of
   controls. */
.event-chev{
  width:1rem;
  height:1rem;
  flex:0 0 auto;
  opacity:.45;
  transition:transform 160ms ease, opacity 160ms ease;
}
.event-row:hover .event-chev,
.event-row:focus-visible .event-chev{opacity:.9;color:var(--clay)}
.event-card[open] .event-chev{transform:rotate(180deg)}

/* -------------------------------------------------------- the open card */

/*
   .event-body, not .event-more. That name was already the "see the whole
   calendar" link at the foot of the front page, and using it twice meant
   this block inherited a 1.75rem top margin it did not ask for while that
   link picked up a 4rem indent it liked even less.
*/
.event-body{
  /* Lined up under the title rather than under the date chip: the date is a
     label on the left margin, and the detail belongs to the words. */
  padding:.1rem 0 1.1rem 4rem;
}

.event-body .event-details{
  margin-top:0;
  color:var(--chalk);
  font-size:.92rem;
  text-align:left;
}

/* .event-facts is written for the bone hero card, where everything runs
   dark. On the felt it has to run light. */
.event-body .event-facts{
  margin-top:.9rem;
  padding-top:.8rem;
  border-top:1px solid rgb(var(--chalk-rgb) / .18);
  color:var(--chalk);
}
.event-body .event-facts a{color:var(--bone);text-decoration-color:var(--clay)}
.event-body .event-facts a:hover{color:var(--clay)}

.event-directions{
  margin-top:.6rem;
  font-size:.9rem;
}
.event-directions a{text-decoration-color:var(--clay)}

.event-body .rsvp.is-compact{margin-top:.9rem}

/*
   An open card is the one you are reading, and it says so.

   Opaque, not a wash: at 3.5% white the ambience glow drifted straight
   through the thing being read and the card was hard to pick out of the
   list at all. A solid panel, a clay edge down the left, and a shadow to
   lift it off the felt -- the same three moves the hero card makes, at a
   quieter volume.
*/
.event-card[open]{
  position:relative;
  background:var(--panel);
  border:1px solid rgba(180,82,46,.45);
  border-left:3px solid var(--clay);
  border-radius:10px;
  box-shadow:0 10px 24px rgba(0,0,0,.42);
}

/* The item's own separator line would draw straight across the top of the
   panel, so it goes while the card is open.

   :has() is the only way to style a parent from a child's state. Anywhere
   it is unsupported the line stays put, which is a stray rule above an
   open card and nothing worse -- the panel, the edge and the shadow all
   still do their job. */
.event-item:has(> .event-card[open]){border-top-color:transparent}

.event-card[open] .event-row{padding:.85rem .9rem}
.event-card[open] .event-body{padding-left:4.9rem;padding-right:.9rem}
.event-card[open] .event-row-title{color:var(--bone)}

/* The date chip is the anchor of the row; on an open card it gets the
   full lift rather than sitting flat. */
.event-card[open] .event-date{box-shadow:0 4px 10px rgba(0,0,0,.45)}

@media (prefers-reduced-motion: reduce){
  .event-chev{transition:none}
}

.event-row.is-cancelled .event-date{opacity:.45}
.event-row.is-cancelled .event-row-title{
  text-decoration:line-through;
  text-decoration-color:var(--clay);
}
.event-row.is-cancelled .event-row-time{opacity:.5}

/* --------------------------------------------------------------- trimmings */

.event-more{
  margin-top:1.75rem;
  font-size:.95rem;
  letter-spacing:.02em;
}

.event-empty{
  margin-top:1.75rem;
  padding:1.25rem;
  border:1px dashed rgb(var(--chalk-rgb) / .3);
  border-radius:10px;
  color:var(--chalk);
  font-size:.95rem;
  text-wrap:balance;
}

/* ------------------------------------------------------------ events page */

.sheet-head{
  margin-bottom:2.25rem;
  text-align:center;
}

.sheet-title{
  font-family:var(--font-display);
  font-weight:400;
  font-size:clamp(2.1rem, 8vw, 2.9rem);
  line-height:1;
  text-shadow:0 3px 0 rgba(0,0,0,.28);
}

.sheet-sub{
  margin-top:.7rem;
  color:var(--chalk);
  text-wrap:balance;
}

.sheet-back{
  margin-top:2.5rem;
  text-align:center;
  font-size:.95rem;
}

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

@media (max-width: 26rem){
  .event-row{
    grid-template-columns:auto 1fr;
    gap:.75rem;
  }
  /* The time moves under the venue rather than squeezing the title, and
     takes the chevron with it. */
  .event-row-time{
    grid-column:2;
    padding-top:.15rem;
  }
  /* The date chip is narrower here, so the indent follows it. */
  .event-body{padding-left:3.85rem}
  .event-card[open] .event-body{padding-left:4.65rem}
  .event-hero{padding:1.75rem 1.15rem 1.5rem}
}

@media (prefers-reduced-motion: reduce){
  .event-hero{animation:none}
}


/* ==================================================================== rsvp */
/*
   The headcount block, in two shapes from one set of rules.
   
   On the hero it sits inside the bone card and everything in it has to run
   dark, the same way .event-hero a does. In a calendar row it sits on the
   felt and runs light. The .rsvp-* colours are therefore never set here;
   they are inherited, and the two host rules below do the deciding.
*/

.rsvp{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:center;
  gap:.6rem .9rem;
  margin-top:1.25rem;
  padding-top:1rem;
  border-top:1px solid rgba(22,17,14,.14);
  font-size:.9rem;
}

.rsvp-count{
  font-weight:600;
  letter-spacing:.02em;
  white-space:nowrap;
}
.rsvp-count .sep{padding:0 .45rem;opacity:.4}
.rsvp-count strong{font-family:var(--font-display);font-weight:400;font-size:1.05rem}

.rsvp-none{font-weight:400;opacity:.6}

.rsvp-form{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:.5rem;
}

/* Taking your name off is a quiet third option, not a third button. Given
   the same weight as "I'm in" it reads as an equal choice, which it is not. */
.rsvp-drop{
  border:0;
  background:none;
  padding:.2rem;
  color:inherit;
  font-family:var(--font-body);
  font-size:.82rem;
  text-decoration:underline;
  text-underline-offset:3px;
  opacity:.6;
  cursor:pointer;
}
.rsvp-drop:hover{opacity:1;color:var(--clay)}

.rsvp-note{
  flex-basis:100%;
  font-size:.82rem;
  opacity:.7;
  text-wrap:balance;
}

/* The compact version, in a calendar row: on the felt, left aligned, and
   pulled tight against the line above it. */
/* Inside an open card it is an ordinary block. On the account page it is
   still a direct child of a grid row -- those lists never became cards --
   so there it has to span the columns it is not the first of. */
li.event-row > .rsvp.is-compact{grid-column:2 / -1}

.rsvp.is-compact{
  justify-content:flex-start;
  margin-top:.55rem;
  padding-top:.55rem;
  border-top:1px solid rgb(var(--chalk-rgb) / .18);
  font-size:.85rem;
}
.rsvp.is-compact .rsvp-note{font-size:.8rem}

/* Count only, nobody signed in: one quiet line, no rule above it and no
   block of space. The row should still read as a row. */
.rsvp.is-compact.is-quiet{
  margin-top:.25rem;
  padding-top:0;
  border-top:0;
  font-size:.82rem;
  opacity:.75;
}
.rsvp.is-compact.is-quiet .rsvp-count strong{font-size:.95rem}

/* The one line on /events that says what the buttons are for. */
.events-rsvp-note{
  margin-top:-1rem;
  margin-bottom:2rem;
  text-align:center;
}

/* On the bone card the buttons' quiet variant has to invert: its border and
   text are set for light-on-felt and vanish against bone. */
.event-hero .rsvp .button-quiet{
  border-color:rgba(22,17,14,.28);
  color:var(--pip);
}
.event-hero .rsvp .button-quiet:hover{
  background:rgba(22,17,14,.06);
  border-color:rgba(22,17,14,.5);
}
.event-hero .rsvp .button:not(.button-quiet){color:var(--bone)}
.event-hero .rsvp-note{color:rgba(22,17,14,.72)}
.event-hero .rsvp-note a{text-decoration:underline;text-underline-offset:3px}

/* ------------------------------------------------------------ who is coming */

/* The host's list, on their own account page. Folded away by default: the
   number is the answer most of the time, and the names are for the night
   before. */
.rsvp-who{
  grid-column:2 / -1;
  margin-top:.55rem;
  padding-top:.55rem;
  border-top:1px solid rgb(var(--chalk-rgb) / .18);
  font-size:.85rem;
}

.rsvp-who summary{
  cursor:pointer;
  color:var(--chalk);
  list-style:none;
}
.rsvp-who summary::-webkit-details-marker{display:none}
.rsvp-who summary::before{
  content:"\25B8";
  display:inline-block;
  width:1em;
  color:var(--clay);
  transition:transform 120ms ease;
}
.rsvp-who[open] summary::before{transform:rotate(90deg)}
.rsvp-who summary strong{color:var(--bone)}
.rsvp-who summary .sep{padding:0 .45rem;opacity:.4}

.rsvp-people{
  list-style:none;
  margin-top:.7rem;
  display:flex;
  flex-direction:column;
  gap:.45rem;
}

.rsvp-person{
  display:flex;
  align-items:center;
  gap:.55rem;
}
.rsvp-person.is-maybe{opacity:.65}

.rsvp-person-name{font-weight:600}

.rsvp-person-phone{
  color:var(--chalk);
  font-size:.82rem;
  font-variant-numeric:tabular-nums;
}

.rsvp-person-tag{
  padding:.05rem .4rem;
  border:1px solid rgb(var(--chalk-rgb) / .4);
  border-radius:3px;
  color:var(--chalk);
  font-size:.62rem;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
}

.rsvp-who .hint{margin-top:.6rem}

@media (max-width: 26rem){
  /* .rsvp-who is still a grid item -- it lives in the account page's own
     rows, which did not become cards. */
  .rsvp-who{grid-column:2 / -1}
  .rsvp-form{width:100%}
}


/* =================================================================== flash */
/*
   The one line result of whatever just happened, across the top of any page.
   Needed once the RSVP buttons started posting from the front page and the
   calendar, neither of which had anywhere to say "you are down for Monday".
*/

.flash{
  position:relative;
  z-index:2;
  display:flex;
  align-items:center;
  justify-content:center;
  width:100%;
  max-width:100vw;
  flex:0 0 auto;
  margin:-2.5rem -1.25rem 2rem;
  padding:.85rem 1.25rem;
  background:var(--felt-lift);
  border-bottom:1px solid rgb(var(--chalk-rgb) / .25);
  color:var(--bone);
  font-size:.9rem;
  line-height:1.45;
  text-align:center;
  text-wrap:pretty;
}
.flash p{max-width:44rem}

/* Under the verify banner rather than lapped over it, when a member who has
   not confirmed their address does something that talks back. */
.verify-banner + .flash{margin-top:-2rem}


/* =================================================================== legal */
/*
   /privacy and /terms. Long prose, which nothing else on this site is, so
   this is the one place that sets a reading measure and spacing for running
   headings and lists.
*/

.legal-doc{
  max-width:38rem;
  margin:0 auto;
  text-align:left;
  text-wrap:pretty;
}

.legal-stamp{
  margin-bottom:2rem;
  padding-bottom:1rem;
  border-bottom:1px solid rgb(var(--chalk-rgb) / .2);
  color:var(--chalk);
  font-size:.8rem;
  letter-spacing:.08em;
  text-transform:uppercase;
}

.legal-doc h2{
  margin-top:2.5rem;
  margin-bottom:.75rem;
  font-family:var(--font-display);
  font-weight:400;
  font-size:1.3rem;
  line-height:1.2;
}
.legal-doc h2:first-of-type{margin-top:0}

.legal-doc h3{
  margin-top:1.75rem;
  margin-bottom:.55rem;
  color:var(--clay);
  font-size:.78rem;
  font-weight:600;
  letter-spacing:.16em;
  text-transform:uppercase;
}

.legal-doc p{margin-top:.9rem}
.legal-doc p:first-child{margin-top:0}

.legal-doc ul{
  margin-top:.9rem;
  padding-left:1.1rem;
  list-style:none;
}

.legal-doc li{
  position:relative;
  margin-top:.6rem;
  padding-left:.35rem;
}
/* A pip rather than a bullet, since the whole site is dominoes. */
.legal-doc li::before{
  content:"";
  position:absolute;
  left:-.9rem;
  top:.62em;
  width:5px;
  height:5px;
  border-radius:50%;
  background:var(--clay);
}

.legal-doc code{
  padding:.1rem .35rem;
  border-radius:3px;
  background:rgba(0,0,0,.22);
  font-size:.9em;
}

.legal-doc a{text-decoration:underline;text-underline-offset:3px}


/* =============================================================== calendar */
/*
   The month grid. Its job is one glance -- is there anything on the second
   Saturday -- so the cells carry the least that answers that, and the list
   underneath carries everything else.

   A grid, not a table. There is no row-and-column relationship here worth
   announcing: a calendar month is a run of days that happens to wrap every
   seven, and a screen reader gets a better account of it from the list below
   than from thirty five table cells.
*/

/* The List / Month toggle, on both calendar pages. */
.view-switch{
  display:flex;
  justify-content:center;
  gap:.25rem;
  width:fit-content;
  margin:-1.25rem auto 1.75rem;
  padding:.2rem;
  border:1px solid rgb(var(--bone-rgb) / .14);
  border-radius:999px;
}

.view-switch a,
.view-switch .view-on{
  padding:.35rem 1rem;
  border-radius:999px;
  font-size:.8rem;
  font-weight:600;
  letter-spacing:.06em;
  text-transform:uppercase;
  text-decoration-color:transparent;
}

.view-switch a{color:var(--chalk)}
.view-switch a:hover,
.view-switch a:focus-visible{
  background:rgb(var(--bone-rgb) / .08);
  color:var(--bone);
}

/* The view you are on is a span, not a link back to itself. */
.view-switch .view-on{
  background:var(--clay);
  color:var(--bone);
}

.cal-nav{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:1.25rem;
  margin-bottom:1.25rem;
}

.cal-now{
  font-family:var(--font-display);
  font-weight:400;
  font-size:1.15rem;
  letter-spacing:.01em;
  min-width:9rem;
  text-align:center;
}

.cal-step{
  padding:.35rem .8rem;
  border:1px solid rgb(var(--bone-rgb) / .18);
  border-radius:999px;
  color:var(--chalk);
  font-size:.82rem;
  font-weight:600;
  letter-spacing:.04em;
  text-decoration-color:transparent;
  white-space:nowrap;
  transition:color 160ms ease, border-color 160ms ease, background-color 160ms ease;
}
.cal-step:hover,
.cal-step:focus-visible{
  border-color:rgb(var(--bone-rgb) / .42);
  background:rgb(var(--bone-rgb) / .07);
  color:var(--bone);
}

.cal-back{
  margin-top:-.6rem;
  margin-bottom:1.25rem;
  text-align:center;
  font-size:.85rem;
}

.cal{margin-bottom:2.5rem}

.cal-week,
.cal-grid{
  display:grid;
  grid-template-columns:repeat(7, 1fr);
  gap:.35rem;
}

.cal-week{margin-bottom:.4rem}

.cal-weekday{
  padding:.2rem 0;
  color:var(--chalk);
  font-size:.66rem;
  font-weight:600;
  letter-spacing:.14em;
  text-align:center;
  text-transform:uppercase;
  opacity:.7;
}

.cal-grid{list-style:none}

.cal-day{
  display:flex;
  flex-direction:column;
  gap:.2rem;
  min-height:5.5rem;
  padding:.35rem .35rem .45rem;
  border:1px solid rgb(var(--chalk-rgb) / .16);
  border-radius:7px;
  background:rgba(0,0,0,.12);
}
.cal-day.is-blank{
  border-color:transparent;
  background:none;
}
.cal-day.is-empty{background:rgba(0,0,0,.06)}
.cal-day.is-past{opacity:.45}

/* Today gets the clay edge the rest of the site uses for "this one". */
.cal-day.is-today{
  border-color:var(--clay);
  background:rgba(180,82,46,.12);
}

.cal-num{
  font-size:.78rem;
  font-weight:600;
  color:var(--chalk);
  font-variant-numeric:tabular-nums;
}
.cal-day.is-today .cal-num{color:var(--bone)}

/* The count is for the narrow layout, where the titles come out. */
.cal-count{display:none}

.cal-ev{
  display:block;
  padding:.2rem .3rem;
  border-radius:4px;
  background:rgb(var(--bone-rgb) / .1);
  color:var(--bone);
  font-size:.7rem;
  line-height:1.25;
  text-decoration-color:transparent;
  transition:background-color 160ms ease;
}
.cal-ev:hover,
.cal-ev:focus-visible{background:var(--clay);color:var(--bone)}
.cal-ev.is-off .cal-ev-name{
  text-decoration:line-through;
  text-decoration-color:var(--clay);
}

.cal-ev-time{
  display:block;
  color:var(--chalk);
  font-size:.62rem;
  font-variant-numeric:tabular-nums;
}
.cal-ev:hover .cal-ev-time{color:var(--bone)}

.cal-ev-name{
  display:block;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

.cal-note{
  margin-top:.75rem;
  margin-bottom:1.5rem;
  text-align:center;
}

/*
   Under about 34rem a seven column grid cannot hold a game's name, so it
   stops trying: the cells shrink to a day number and a count, and the list
   below becomes the way you read the month. Squeezing three ellipsised
   characters into a cell would be a grid that looks right and says nothing.
*/
@media (max-width: 34rem){
  .cal-week,
  .cal-grid{gap:.2rem}

  .cal-day{
    min-height:3.1rem;
    padding:.25rem .15rem;
    align-items:center;
    justify-content:flex-start;
  }

  .cal-num{font-size:.8rem}

  .cal-count{
    display:block;
    margin-top:.15rem;
    padding:.05rem .28rem;
    border-radius:999px;
    background:var(--clay);
    color:var(--bone);
    font-size:.58rem;
    font-weight:600;
    line-height:1.5;
  }
  .cal-count-word{display:none}

  .cal-ev{display:none}

  .cal-weekday{font-size:.58rem;letter-spacing:.08em}

  .cal-nav{gap:.6rem}
  .cal-now{min-width:0;font-size:1rem}
}


/* ================================================================== forms */
/*
   Inputs are bone on felt, the same two colours as everything else. They sit
   flat rather than raised, so a form never competes with an event card for
   the eye: the cards are the tiles on the table, the form is the paper you
   fill in beside it.
*/

.form-block{
  margin-bottom:2rem;
  padding-bottom:1.75rem;
  border:0;
  border-bottom:1px solid rgb(var(--chalk-rgb) / .18);
}
.form-block:last-of-type{border-bottom:0}

.form-block legend{
  padding:0;
  margin-bottom:1.1rem;
  color:var(--chalk);
  font-size:.72rem;
  font-weight:600;
  letter-spacing:.2em;
  text-transform:uppercase;
}

.field{margin-bottom:1.1rem}
.field:last-child{margin-bottom:0}

.field label{
  display:block;
  margin-bottom:.35rem;
  font-size:.92rem;
  font-weight:600;
}

.hint{
  color:var(--chalk);
  font-size:.82rem;
  font-weight:400;
  letter-spacing:0;
  text-transform:none;
}
.field .hint{display:block;margin-top:.3rem}
.field label .hint{display:inline;margin:0 0 0 .35rem;opacity:.75}

.field input[type=text],
.field input[type=email],
.field input[type=tel],
.field input[type=url],
.field input[type=date],
.field input[type=time],
.field input[type=password],
.field select,
.field textarea{
  width:100%;
  padding:.6rem .7rem;
  border:1px solid rgb(var(--bone-rgb) / .22);
  border-radius:6px;
  background:rgba(18,36,29,.55);
  color:var(--bone);
  font-family:var(--font-body);
  font-size:1rem;
  line-height:1.4;
}

.field textarea{resize:vertical;min-height:6rem}

.field input::placeholder,
.field textarea::placeholder{color:rgb(var(--chalk-rgb) / .45)}

.field input:focus,
.field select:focus,
.field textarea:focus{
  outline:2px solid var(--clay);
  outline-offset:1px;
  border-color:transparent;
}

/* Date and time pickers hand the browser a bone coloured icon rather than
   the default dark one, which is invisible on this background. */
.field input[type=date]::-webkit-calendar-picker-indicator,
.field input[type=time]::-webkit-calendar-picker-indicator{
  filter:invert(89%) sepia(11%) saturate(347%) hue-rotate(3deg) brightness(97%);
  cursor:pointer;
}

.field.is-bad input,
.field.is-bad select,
.field.is-bad textarea{border-color:var(--clay)}

.field-error{
  display:block;
  margin-top:.35rem;
  color:var(--clay);
  font-size:.85rem;
  font-weight:600;
}

/* Two fields side by side on anything wider than a phone. */
.field-pair{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:0 1rem;
}

.form-hint{
  margin-bottom:1.1rem;
  color:var(--chalk);
  font-size:.9rem;
  text-wrap:pretty;
}

.form-notice{
  margin-bottom:1.5rem;
  padding:.85rem 1rem;
  border-left:3px solid var(--clay);
  border-radius:0 6px 6px 0;
  background:rgba(180,82,46,.14);
  font-size:.95rem;
  text-wrap:pretty;
}
.form-notice.is-good{
  border-left-color:var(--chalk);
  background:rgb(var(--chalk-rgb) / .12);
}

/* The honeypot. Off screen rather than display:none, which some bots
   check for before deciding whether to fill a field in. */
.guard{
  position:absolute;
  left:-9999px;
  width:1px;
  height:1px;
  overflow:hidden;
}

/* ------------------------------------------------------------ once/weekly */

.choice{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:.75rem;
  margin-bottom:1.4rem;
}

.choice-option{
  display:block;
  padding:.9rem 1rem;
  border:1px solid rgb(var(--bone-rgb) / .22);
  border-radius:8px;
  background:rgba(18,36,29,.4);
  cursor:pointer;
}
.choice-option:hover{border-color:rgb(var(--bone-rgb) / .4)}

.choice-option input{
  margin-right:.5rem;
  accent-color:var(--clay);
}

.choice-title{font-weight:600}

.choice-note{
  display:block;
  margin-top:.25rem;
  color:var(--chalk);
  font-size:.85rem;
  line-height:1.45;
}

/* The picked one lifts, so it is obvious which set of fields is live. */
.choice-option:has(input:checked){
  border-color:var(--clay);
  background:rgba(180,82,46,.12);
}

.when-block[hidden]{display:none}

/* ------------------------------------------------------------ maps lookup */

/* The pasted link and its Fill in button on one line, with the button
   staying its own width and the field taking the rest. */
.maps-row{
  display:flex;
  gap:.5rem;
  align-items:stretch;
}
.maps-row input[type=text]{flex:1;min-width:0}
.maps-row .button{
  flex:0 0 auto;
  white-space:nowrap;
}

/* Off screen and first in the form, so pressing Enter in a text field still
   means "send" rather than firing the Fill in button that happens to come
   before the real one in the markup. */
.default-submit{
  position:absolute;
  left:-9999px;
  width:1px;
  height:1px;
  overflow:hidden;
}

@media (max-width: 26rem){
  /* Two thumbs' worth of field beats a field and a button crammed side by
     side, so the button drops under it. */
  .maps-row{flex-direction:column}
  .maps-row .button{align-self:flex-start}
}

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

.button{
  display:inline-block;
  padding:.65rem 1.4rem;
  border:1px solid var(--clay);
  border-radius:6px;
  background:var(--clay);
  color:var(--bone);
  font-family:var(--font-body);
  font-size:.95rem;
  font-weight:600;
  letter-spacing:.02em;
  line-height:1.3;
  text-decoration:none;
  cursor:pointer;
}
.button:hover{background:#c65c34;border-color:#c65c34;color:var(--bone)}
.button:focus-visible{outline:2px solid var(--bone);outline-offset:2px}

.button-quiet{
  background:transparent;
  border-color:rgb(var(--bone-rgb) / .3);
  color:var(--bone);
}
.button-quiet:hover{
  background:rgb(var(--bone-rgb) / .08);
  border-color:rgb(var(--bone-rgb) / .55);
  color:var(--bone);
}

.button-small{padding:.4rem .8rem;font-size:.85rem}

.form-send{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:.9rem;
  margin-top:1.75rem;
}

/* ================================================================ sign in */

.signin{max-width:22rem;margin:0 auto;text-align:center}

.signin .tile{width:min(8rem,40vw);margin-bottom:1.75rem}

.signin-title{
  margin-bottom:1.75rem;
  font-family:var(--font-display);
  font-weight:400;
  font-size:1.9rem;
}

.signin-form{text-align:left}
.signin-form .form-send{justify-content:center}

.signin-help{
  margin-top:1rem;
  color:var(--chalk);
  font-size:.9rem;
  text-align:left;
  text-wrap:pretty;
}

.signin-code{
  margin-top:.75rem;
  padding:.75rem;
  border-radius:6px;
  background:rgba(18,36,29,.7);
  color:var(--bone);
  font-family:ui-monospace, "Cascadia Code", Consolas, monospace;
  font-size:.75rem;
  text-align:left;
  overflow-x:auto;
}

.signin-back{margin-top:2rem;font-size:.9rem}

/* =============================================================== backstage */

.desk-head{position:relative}

.desk-out{
  position:absolute;
  top:0;
  right:0;
}

/* The mirror of the sign out button, on the other corner. Both are pinned
   to the header rather than sitting in the flow, so the title stays
   centred between them. */
.desk-add{
  position:absolute;
  top:0;
  left:0;
}

/* Three buttons on one side and one on the other stopped fitting either
   side of a centred title somewhere under 700px -- "Download current CSV
   template" is a long label, and a pinned corner has no way to give: it
   just slid under Sign out. Below this they sit under the title instead,
   which is what the phone layout already did. The number is where the four
   of them measured out, not a round guess. */
@media (max-width: 46rem){
  .desk-out,
  .desk-add{position:static;margin-top:1rem}
}

.desk-head-2{
  display:flex;
  align-items:center;
  gap:.9rem;
  margin:2.5rem 0 1rem;
  color:var(--chalk);
  font-size:.72rem;
  font-weight:600;
  letter-spacing:.2em;
  text-transform:uppercase;
}
.desk-head-2::after{
  content:"";
  flex:1;
  height:1px;
  background:linear-gradient(to right, rgb(var(--chalk-rgb) / .4), transparent);
}

.desk-note{
  margin-bottom:1rem;
  color:var(--chalk);
  font-size:.88rem;
  text-wrap:pretty;
}

/* The offer to place listings that have no pin. It wears .surface-sm in the
   markup -- a line of prose that wraps is exactly what that shape is for --
   so all this adds is the row: flex and wrap, so the button drops under the
   sentence on a phone rather than squeezing it. */
.desk-place{
  display:flex;
  align-items:center;
  flex-wrap:wrap;
  gap:.75rem;
  margin-bottom:1rem;
}
.desk-place-said{
  color:var(--chalk);
  font-size:.88rem;
}

/* A submission waiting on a decision. Bordered rather than bone, so the
   queue never looks like the published calendar. */
.desk-card{
  margin-bottom:1rem;
  padding:1.25rem 1.35rem;
  border:1px solid rgb(var(--chalk-rgb) / .28);
  border-radius:10px;
  background:rgba(18,36,29,.35);
}

.desk-flag{
  margin-bottom:.5rem;
  color:var(--clay);
  font-size:.7rem;
  font-weight:600;
  letter-spacing:.18em;
  text-transform:uppercase;
}

.desk-title{
  font-family:var(--font-display);
  font-weight:400;
  font-size:1.3rem;
  line-height:1.2;
  margin-bottom:.5rem;
}

.desk-line{font-size:.95rem;line-height:1.5}
.desk-line .sep{padding:0 .45rem;opacity:.4}

.desk-summary{
  margin-top:.6rem;
  color:var(--chalk);
  font-size:.92rem;
  text-wrap:pretty;
}

.desk-details{
  margin-top:.5rem;
  padding-left:.85rem;
  border-left:2px solid rgb(var(--chalk-rgb) / .25);
  color:var(--chalk);
  font-size:.88rem;
  text-wrap:pretty;
}

.desk-meta{
  margin-top:.9rem;
  padding-top:.75rem;
  border-top:1px solid rgb(var(--chalk-rgb) / .16);
  color:var(--chalk);
  font-size:.85rem;
}
.desk-meta .sep{padding:0 .45rem;opacity:.4}

.desk-quiet{opacity:.55}

.desk-actions{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:.6rem;
  margin-top:1.1rem;
}
.desk-actions form{display:flex;gap:.5rem;align-items:center}

.desk-reject input[type=text]{
  width:11rem;
  padding:.4rem .6rem;
  border:1px solid rgb(var(--bone-rgb) / .22);
  border-radius:6px;
  background:rgba(18,36,29,.55);
  color:var(--bone);
  font-family:var(--font-body);
  font-size:.85rem;
}
.desk-reject input[type=text]:focus{
  outline:2px solid var(--clay);
  outline-offset:1px;
}

/* Rows in the manage lists carry their actions on the right instead of a
   time, so they get a column of their own. */
.event-row.desk-row{align-items:center}
.event-row.desk-row .desk-actions{margin-top:0}

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

@media (max-width: 34rem){
  .field-pair,
  .choice{grid-template-columns:1fr}
  .field-pair .field{margin-bottom:1.1rem}

  .event-row.desk-row{
    grid-template-columns:auto 1fr;
    row-gap:.6rem;
  }
  .event-row.desk-row .desk-actions{grid-column:1 / -1}

  .desk-reject{flex-wrap:wrap}
  .desk-reject input[type=text]{width:100%}
}


/* ================================================================ accounts */

/* Sign in and join are short forms. A full width column makes two fields
   look stranded, so these pages pull the measure in. */
.sheet.narrow{
  max-width:24rem;
  margin:0 auto;
}

.account-line{
  margin-top:.5rem;
  font-size:.78rem;
  opacity:.85;
}

/* The status of one of a member's own submissions. */
.state{
  align-self:center;
  padding:.2rem .6rem;
  border:1px solid currentColor;
  border-radius:999px;
  font-size:.7rem;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
  white-space:nowrap;
}
.state.is-live{color:var(--chalk)}
.state.is-wait{color:var(--clay)}
.state.is-off{color:rgb(var(--chalk-rgb) / .5)}

/* --------------------------------------------------------------- reports */

/* A report is mostly one block of somebody else's prose, so it gets a
   quieter card than a submission: no title line to hang a heading on, and
   the message itself doing the work. */
.report-card .desk-flag{text-transform:none;letter-spacing:.04em}

.report-message{
  margin-top:.6rem;
  padding-left:.9rem;
  border-left:2px solid rgba(180,82,46,.5);
  text-wrap:pretty;
  white-space:normal;
  overflow-wrap:anywhere;
}

/* Already dealt with: still readable, no longer asking for attention. */
.report-card.is-done,
.report-card.is-spam{opacity:.6}
.report-card.is-spam .report-message{border-left-color:rgb(var(--chalk-rgb) / .35)}

.report-flags{color:var(--clay)}

.report-agent{
  font-size:.72rem;
  overflow-wrap:anywhere;
}

/* The account an entry in the queue belongs to, under the contact line. */
.desk-owner{
  margin-top:.4rem;
  padding-top:0;
  border-top:0;
  font-size:.82rem;
}

@media (max-width: 34rem){
  .state{justify-self:start}
}


/* ========================================================= verify banner */

.verify-banner{
  position:relative;
  z-index:2;
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:center;
  gap:.75rem 1.25rem;
  width:100%;
  margin:-2.5rem -1.25rem 2rem;
  padding:.85rem 1.25rem;
  /* Pulls itself out to the full window width, past body's padding. */
  max-width:100vw;
  flex:0 0 auto;
  background:var(--clay);
  /* Clay in both themes, so its text does not follow the palette. */
  color:var(--on-clay);
  font-size:.9rem;
  line-height:1.45;
  text-align:center;
  text-wrap:pretty;
}
.verify-banner p{max-width:44rem}
.verify-banner .button{
  border-color:rgb(var(--bone-rgb) / .55);
  background:transparent;
  white-space:nowrap;
}
.verify-banner .button:hover{
  background:rgb(var(--bone-rgb) / .15);
  border-color:var(--bone);
}

/* ================================================================ avatars */

.avatar{
  width:var(--avatar-size, 48px);
  height:var(--avatar-size, 48px);
  border-radius:50%;
  object-fit:cover;
  background:var(--card);
  box-shadow:0 2px 8px rgba(0,0,0,.35);
  vertical-align:middle;
}

.avatar.is-empty .avatar-bg{fill:rgb(var(--bone-rgb) / .14)}
.avatar.is-empty .avatar-figure{fill:rgb(var(--chalk-rgb) / .55)}
.avatar.is-empty{box-shadow:none;background:transparent}

.account-link{
  display:inline-flex;
  align-items:center;
  gap:.45rem;
  text-decoration-color:transparent;
}
.account-link:hover{text-decoration-color:var(--clay)}
.account-link .avatar{box-shadow:none}

.avatar-panel{
  display:flex;
  align-items:flex-start;
  gap:1.5rem;
  margin-bottom:1.5rem;
}
.avatar-current{flex:0 0 auto}
.avatar-controls{flex:1;min-width:0}
.avatar-controls .form-send{margin-top:1rem}
.avatar-controls .form-send form{display:inline}

/* ---------------------------------------------------------------- cropper */

.cropper{
  margin:0 0 2rem;
  padding:1.25rem;
  border:1px solid rgb(var(--chalk-rgb) / .28);
  border-radius:10px;
  background:rgba(18,36,29,.35);
  text-align:center;
}

.cropper-stage{
  position:relative;
  width:320px;
  max-width:100%;
  margin:0 auto;
  aspect-ratio:1;
  border-radius:8px;
  overflow:hidden;
  touch-action:none;   /* the canvas handles dragging itself */
}

.cropper-stage canvas{
  display:block;
  width:100%;
  height:100%;
  cursor:grab;
}
.cropper-stage canvas:active{cursor:grabbing}

/* The circle the picture will actually be shown in, dimmed outside. */
/*
   The circle showing what will actually be kept. Its diameter is the full
   width of the square, because that is what the crop is: the canvas is the
   picture, and the avatar is that picture with a round corner radius.

   Drawn as a circle carrying an enormous spread shadow, which the stage's
   overflow:hidden clips back to the square. The unpainted part is exactly
   the circle. A radial-gradient mask was doing this before and was wrong:
   percentages there are measured along the gradient ray, which defaults to
   the farthest CORNER, so "50%" came out as half the diagonal and drew a
   circle about 71% of the width.
*/
.cropper-mask{
  position:absolute;
  inset:0;
  pointer-events:none;
  border-radius:50%;
  border:1px solid rgb(var(--bone-rgb) / .4);
  box-shadow:0 0 0 9999px rgba(18,36,29,.62);
}

.cropper-zoom{
  max-width:320px;
  margin:1.1rem auto 0;
  text-align:left;
}
.cropper-zoom input[type=range]{
  width:100%;
  accent-color:var(--clay);
}

.cropper .form-send{justify-content:center}

/* ------------------------------------------------------------- checkboxes */

.check-line{
  display:flex;
  align-items:flex-start;
  gap:.65rem;
  font-weight:400;
  cursor:pointer;
}
.check-line input[type=checkbox]{
  flex:0 0 auto;
  width:1.05rem;
  height:1.05rem;
  margin-top:.2rem;
  accent-color:var(--clay);
}
.check-line > span{flex:1}
.field.check label{margin-bottom:0}

.newsletter-form{
  margin-top:1.5rem;
  padding-top:1.25rem;
  border-top:1px solid rgb(var(--chalk-rgb) / .18);
}
.newsletter-form .form-send{margin-top:.75rem}

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

@media (max-width: 34rem){
  .verify-banner{
    margin:-2.5rem -1.25rem 1.5rem;
    text-align:center;
  }

  .avatar-panel{
    flex-direction:column;
    align-items:center;
    text-align:center;
  }
  .avatar-controls .form-send{justify-content:center}
}


/* ================================================================= chrome */
/*
   Everything that sits above the page proper: who you are, and any banner.
   One wrapper owns pulling the strip out to the window edges, so the pieces
   inside it are ordinary blocks that stack in order.
*/

.chrome{
  position:relative;
  z-index:2;
  align-self:stretch;
  /* Out past body's own padding, so the corner really is the corner. */
  margin:-2.5rem -1.25rem 1.5rem;
}

/*
   min-height is doing real work here, not spacing. The corner below is
   position:fixed, so it is outside the flow and nothing else reserves room
   for it -- this strip is the only thing keeping it off the first heading
   on a page at rest.
   
   The number is measured, not chosen: the corner is about 96px from the top
   of the window to the bottom of the name, and this plus .chrome's bottom
   margin has to clear that. Shrink either and a long centred title like
   "Report a problem" runs straight under it on a phone, while a short one
   like "Events" hides the bug -- which is exactly how it got missed first
   time round.
*/
.topbar{
  display:flex;
  align-items:center;
  gap:.75rem;
  min-height:5rem;
  padding:.6rem 1.25rem;
}

/* The topbar now carries only the way back home -- who you are moved out to
   the fixed corner below. It keeps its min-height regardless, because that
   reserved band is what stops the corner landing on the first heading. */

.topbar-link{
  color:var(--chalk);
  font-size:.85rem;
  font-weight:600;
  letter-spacing:.06em;
  text-decoration-color:transparent;
  text-transform:uppercase;
  transition:color 160ms ease;
}
.topbar-link:hover,
.topbar-link:focus-visible{
  color:var(--bone);
  text-decoration-color:var(--clay);
}

/* Bare pages skip the chrome in PHP; this is only a backstop. */
body.bare .chrome{display:none}

/* Backstage skips the corner in PHP, so on those pages that 5rem band is
   reserving room for something that is not coming and reads as a gap over
   the header. This is the one place it is safe to take it back -- everywhere
   else the corner is still fixed over it. */
body.backstage .topbar{min-height:0}


/* ================================================================= corner */
/*
   Who you are, pinned to the top right and staying there. It used to sit in
   the topbar and scroll away with it, which on a page that is mostly a long
   list meant the way back to your own account depended on remembering to
   scroll up.

   Signed in and signed out are the same shape -- picture above, word below --
   so the corner does not change size when you sign in and the page does not
   appear to shift under you.
*/

.corner{
  position:fixed;
  top:.7rem;
  right:.9rem;
  z-index:30;
}

.corner-user{
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:.3rem;
  width:5.25rem;
  padding:.35rem .3rem .4rem;
  border:1px solid transparent;
  border-radius:16px;
  color:var(--bone);
  font-size:.72rem;
  font-weight:600;
  letter-spacing:.04em;
  text-align:center;
  text-decoration-color:transparent;
  transition:border-color 160ms ease, background-color 160ms ease;
}
.corner-user:hover,
.corner-user:focus-visible{
  border-color:rgb(var(--bone-rgb) / .26);
  /* Felt rather than a light wash: it floats over the page once you scroll,
     and a pale pill would read as a hole punched in it. */
  background:rgba(18,36,29,.82);
  color:var(--bone);
}

.corner-avatar{
  flex:0 0 auto;
  /* Enough of a lift to stay separate from whatever it is floating over. */
  box-shadow:0 4px 14px rgba(0,0,0,.5);
}
/* The silhouette needs an edge here, or it reads as a hole in the felt
   rather than as a picture that has not been set yet. */
.corner-avatar.is-empty .avatar-bg{fill:rgb(var(--bone-rgb) / .16)}
.corner-user:hover .corner-avatar.is-empty .avatar-bg{fill:rgb(var(--bone-rgb) / .26)}

.corner-name{
  max-width:100%;
  /* Tight, because this is one word under a picture and the body's 1.6
     would put a line's worth of air under it -- which the strip above then
     has to reserve room for. */
  line-height:1.2;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
  /* The name sits on the page, not on the button, so it needs its own
     legibility rather than borrowing the hover panel's. */
  text-shadow:0 1px 3px rgba(0,0,0,.65);
}
.corner-user.is-out .corner-name{
  color:var(--chalk);
  text-transform:uppercase;
}
.corner-user.is-out:hover .corner-name{color:var(--bone)}

/* The home page's tile is the first thing that should land on the eye, so
   the corner sits a little quieter there. */
body.home .topbar-link,
body.home .corner-user{opacity:.88}
body.home .topbar-link:hover,
body.home .corner-user:hover{opacity:1}

/* Both banners run the full width, so they need to keep clear of the corner
   rather than run underneath it. */
.verify-banner,
.flash{padding-right:7rem}


/* ============================================================= bug button */
/*
   Bottom right, on every page including the 404 -- which is exactly where
   somebody wants to say a link is broken and the one page that cannot tell
   them anything useful itself.

   A link to a real page, not a panel that unfolds. Somebody reporting that
   the site is broken is the last person who should be handed a control that
   depends on the site working.
*/

.bug-button{
  position:fixed;
  right:.9rem;
  bottom:.9rem;
  z-index:30;
  display:inline-flex;
  align-items:center;
  gap:.45rem;
  padding:.5rem .85rem .5rem .7rem;
  border:1px solid rgb(var(--bone-rgb) / .2);
  border-radius:999px;
  background:rgba(18,36,29,.86);
  color:var(--chalk);
  font-size:.78rem;
  font-weight:600;
  letter-spacing:.04em;
  text-decoration-color:transparent;
  box-shadow:0 4px 14px rgba(0,0,0,.4);
  transition:color 160ms ease, border-color 160ms ease, background-color 160ms ease;
}
.bug-button:hover,
.bug-button:focus-visible{
  border-color:rgba(180,82,46,.75);
  background:var(--felt-deep);
  color:var(--bone);
}

.bug-button svg{
  width:1.05rem;
  height:1.05rem;
  flex:0 0 auto;
  color:var(--clay);
}

/* So the footer's last line and the staff mark are not sat under the
   button when the page is scrolled to the bottom. */
.site-footer{padding-bottom:3.5rem}

@media (max-width: 34rem){
  .chrome{margin:-2.5rem -1.25rem 1.25rem}
  /* The corner is smaller here, but not by much -- the picture is the same
     size and only the name shrinks -- so the strip keeps most of its
     height. See the note on .topbar above. */
  .topbar{min-height:4.75rem;padding:.5rem 1rem}

  /* Smaller, so it still fits inside the reserved band on a phone. */
  .corner{top:.5rem;right:.6rem}
  .corner-user{width:4.4rem;font-size:.66rem}

  .verify-banner,
  .flash{padding-right:5.5rem}

  /* The label goes and the button becomes the icon. On a phone the bottom
     right corner is thumb country and a wide pill sits over the content. */
  .bug-button{padding:.6rem;right:.7rem;bottom:.7rem}
  .bug-button-label{
    position:absolute;
    width:1px;
    height:1px;
    overflow:hidden;
    clip-path:inset(50%);
    white-space:nowrap;
  }
  .bug-button svg{width:1.2rem;height:1.2rem}
}


/* ======================================================= password reveal */

.reveal{
  position:relative;
  display:block;
}

/* Room for the button, so a long password never runs under the eye. */
.field.has-reveal .reveal input{padding-right:3rem}

.reveal-toggle{
  position:absolute;
  top:50%;
  right:.35rem;
  transform:translateY(-50%);
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:2.25rem;
  height:2.25rem;
  padding:0;
  border:0;
  border-radius:6px;
  background:transparent;
  color:var(--chalk);
  cursor:pointer;
  transition:color 150ms ease, background-color 150ms ease;
}
.reveal-toggle:hover{
  color:var(--bone);
  background:rgb(var(--bone-rgb) / .08);
}
.reveal-toggle:focus-visible{
  outline:2px solid var(--clay);
  outline-offset:1px;
}
.reveal-toggle[hidden]{display:none}

.reveal-toggle svg{
  width:1.2rem;
  height:1.2rem;
  display:block;
}

/* The slash only appears once the password is showing, so the icon says
   what pressing it will do rather than what state you are in. */
.reveal-slash{
  opacity:0;
  transition:opacity 150ms ease;
}
.reveal-toggle[aria-pressed="true"] .reveal-slash{opacity:1}
.reveal-toggle[aria-pressed="true"]{color:var(--clay)}

@media (prefers-reduced-motion: reduce){
  .reveal-toggle,
  .reveal-slash{transition:none}
}


/* ============================================================ home button */

.topbar-home{
  display:inline-flex;
  align-items:center;
  gap:.3rem;
  padding:.35rem .7rem .35rem .45rem;
  border:1px solid rgb(var(--bone-rgb) / .16);
  border-radius:999px;
  color:var(--chalk);
  font-size:.82rem;
  font-weight:600;
  letter-spacing:.04em;
  text-decoration-color:transparent;
  transition:color 160ms ease, border-color 160ms ease, background-color 160ms ease;
}
.topbar-home:hover,
.topbar-home:focus-visible{
  border-color:rgb(var(--bone-rgb) / .4);
  background:rgb(var(--bone-rgb) / .07);
  color:var(--bone);
}
.topbar-home svg{
  width:1rem;
  height:1rem;
  flex:0 0 auto;
}
/* The arrow leads the way out on hover. */
.topbar-home svg{transition:transform 160ms ease}
.topbar-home:hover svg{transform:translateX(-2px)}

/* ============================================================= home card */
/*
   The front page's words sit on a raised panel rather than straight on the
   felt: a tile laid on a table reads as an object, and loose text next to
   it reads as a caption. Kept dark and translucent instead of bone, so the
   page does not turn into a white sheet and fight the event cards for
   attention.
*/

body.home .card{
  padding:2.5rem 1.6rem 2.25rem;
  border:1px solid rgb(var(--bone-rgb) / .14);
  border-radius:18px;
  background:rgba(18,36,29,.44);
  box-shadow:var(--home-shadow);
}

/* The tile hangs over the top edge, so the card reads as something the
   tile was laid on rather than a box the tile is inside. */
body.home .card > .tile{margin-top:-3.75rem}

body.home .wordmark{margin-top:1.25rem}

/* The note already has a rule above it; on the card the card's own edge is
   close enough that a second line is clutter. */
body.home .note{
  margin-top:2rem;
  padding-top:1.5rem;
}

@media (max-width: 34rem){
  body.home .card{
    padding:2rem 1.15rem 1.75rem;
    border-radius:14px;
  }
  body.home .card > .tile{margin-top:-3rem}
}

/* ======================================================== felt, breathing */
/*
   A slow drift of light across the table. Big, soft and very low contrast:
   at any one moment it should be impossible to point at, and only obvious
   if you look away and back.

   A transform on a fixed layer, so it composites on the GPU and never
   causes the page itself to re-lay-out.
*/


/* The grain drifts the other way and much less, so the two never look like
   one sheet sliding about. */
body::before{
  animation:grain-drift 90s linear infinite alternate;
}

@keyframes grain-drift{
  from{transform:translate3d(0, 0, 0)}
  to{transform:translate3d(-1.5%, 1.5%, 0)}
}

@media (prefers-reduced-motion: reduce){
  body::before,
  .glow{animation:none}
  .topbar-home svg{transition:none}
}


/* ============================================================== ambience */
/*
   Light moving over the table: the felt's own green, and the warm cast of a
   lamp above it.

   Each is a soft radial with a transparent falloff rather than a hard shape
   behind a blur filter -- a blur this large is one of the few things that
   can still make a phone drop frames, and the gradient gets there free.

   Plain alpha, no blend mode. mix-blend-mode was tried and does nothing
   useful here: .ambience is a stacking context of its own, so the blend
   composites against its own transparent inside rather than against the
   felt, which is an expensive way to arrive back at ordinary alpha.

   They orbit rather than slide. Rotate, translate, then counter-rotate:
   each layer travels a circle while staying upright, which reads as a slow
   swirl instead of a rectangle sliding about. Five periods, none a multiple
   of another, so the pattern takes hours to repeat.
*/

.ambience{
  position:fixed;
  inset:0;
  z-index:0;
  overflow:hidden;
  pointer-events:none;
  /* Its own paint context, so none of this can force the page above it to
     repaint. */
  contain:strict;
}

.glow{
  position:absolute;
  display:block;
  border-radius:50%;
  will-change:transform;
}

/* ---- the felt's own colour ---- */

.glow-felt-a{
  width:120vmax;
  height:120vmax;
  left:-30vmax;
  top:-45vmax;
  background:radial-gradient(closest-side circle,
    rgb(var(--glow-felt-rgb) / .75), rgb(var(--glow-felt-rgb) / 0) 70%);
  animation:orbit-a 96s linear infinite;
}

.glow-felt-b{
  width:95vmax;
  height:95vmax;
  right:-28vmax;
  bottom:-38vmax;
  background:radial-gradient(closest-side circle,
    rgb(var(--glow-felt-rgb) / .55), rgb(var(--glow-felt-rgb) / 0) 72%);
  animation:orbit-b 134s linear infinite;
}

/* ---- lamplight ----
   Clay, kept well under half opacity and spread very wide. Concentrated it
   would look like orange paint; spread across most of the window it reads
   as a warm room. Placed to cross the middle of the page rather than hug
   the corners, because the corners are where nobody is looking. */

.glow-warm-a{
  width:90vmax;
  height:90vmax;
  left:-8vmax;
  top:-32vmax;
  background:radial-gradient(closest-side circle,
    rgb(var(--glow-warm-rgb) / .58), rgb(var(--glow-warm-deep-rgb) / .20) 45%,
    rgb(var(--glow-warm-deep-rgb) / 0) 72%);
  opacity:.9;
  animation:orbit-c 112s linear infinite;
}

.glow-warm-b{
  width:68vmax;
  height:68vmax;
  right:-16vmax;
  top:22vmax;
  background:radial-gradient(closest-side circle,
    rgb(var(--glow-warm-rgb) / .50), rgb(var(--glow-warm-deep-rgb) / .17) 46%,
    rgb(var(--glow-warm-deep-rgb) / 0) 74%);
  opacity:.85;
  animation:orbit-d 158s linear infinite reverse;
}

/* A third, smaller and slower, low on the page. Two blobs orbiting read as
   two blobs; three read as weather. */
.glow-warm-c{
  width:54vmax;
  height:54vmax;
  left:16vmax;
  bottom:-24vmax;
  background:radial-gradient(closest-side circle,
    rgb(var(--glow-warm-rgb) / .42), rgb(var(--glow-warm-deep-rgb) / .13) 48%,
    rgb(var(--glow-warm-deep-rgb) / 0) 74%);
  opacity:.75;
  animation:orbit-e 186s linear infinite;
}

/* The counter-rotation is what keeps each layer upright while its centre
   travels a circle. Without it they tumble, which looks like a mistake. */
@keyframes orbit-a{
  from{transform:rotate(0deg)    translate3d(7vmax, 0, 0) rotate(0deg)}
  to  {transform:rotate(360deg)  translate3d(7vmax, 0, 0) rotate(-360deg)}
}
@keyframes orbit-b{
  from{transform:rotate(0deg)    translate3d(9vmax, 0, 0) rotate(0deg)}
  to  {transform:rotate(-360deg) translate3d(9vmax, 0, 0) rotate(360deg)}
}
@keyframes orbit-c{
  from{transform:rotate(0deg)    translate3d(13vmax, 0, 0) rotate(0deg)   scale(1)}
  to  {transform:rotate(360deg)  translate3d(13vmax, 0, 0) rotate(-360deg) scale(1.14)}
}
@keyframes orbit-d{
  from{transform:rotate(0deg)    translate3d(15vmax, 0, 0) rotate(0deg)}
  to  {transform:rotate(360deg)  translate3d(15vmax, 0, 0) rotate(-360deg)}
}
@keyframes orbit-e{
  from{transform:rotate(0deg)    translate3d(11vmax, 0, 0) rotate(0deg)   scale(1.08)}
  to  {transform:rotate(-360deg) translate3d(11vmax, 0, 0) rotate(360deg)  scale(1)}
}

/* The front page is the one that should feel warm. Deeper in, the lamp is
   turned down so it never competes with a form or a list of dates. */
body:not(.home) .glow-warm-a{opacity:.42}
body:not(.home) .glow-warm-b{opacity:.36}
body:not(.home) .glow-warm-c{opacity:.3}


/* ==================================================================== cards */
/*
   Everything that is words sits on a bone card, and the felt only shows in
   the gaps between them.

   The felt is not a flat colour -- there is a grain layer and five drifting
   glows over it -- so text laid straight on it was being read against
   weather. The open event card had already been made opaque for exactly
   that reason; this is the same decision applied to the rest of the site.

   Blocks, not one sheet. A single panel behind the whole page would put the
   month grid, the list under it and the footer inside one slab and lose the
   separation between them. Each block gets its own card instead: the page
   header, the grid, every event row, each form, the footer.

   HOW THE INVERSION WORKS, because it is the whole trick here and it is not
   obvious from the rules below.

   The stylesheet above is written as "bone text on felt", and says so about
   two hundred times -- as var(--bone), var(--chalk), and as those two at an
   alpha for every hairline and hover wash. Rather than fork each of those,
   a card rebinds the four properties they are all written in terms of, so
   the same rules resolve to "ink on bone" inside one. That is why the rgba()
   literals became rgb(var(--*-rgb) / a) further up.

   What it cannot do on its own, and what the patches at the foot of this
   section are for:

     - anything sitting on CLAY. Clay is clay on either surface, so its text
       must not follow the flip. That is --on-clay.
     - anything using --bone as a BACKGROUND or an SVG fill rather than as
       text: the domino faces, the date chip, the avatar disc. Those invert
       correctly and then need their contents put back the other way.
     - the deliberately dark panels -- form inputs, the recovery code block --
       which have to become light sunken ones instead of inverting.
*/

/* ------------------------------------------------------------ the surface */

/*
   One list, three shapes. Everything named here flips its palette; what
   differs is only how much padding and how round the corner is.
*/
.surface,
body.home .card,
body.bare .card,
.signin,
.sheet-head,
.cal,
.corner-panel,
.event-hero,
.event-item,
li.event-row,
.event-empty,
.desk-card,
.legal-doc,
form.form,
.avatar-panel,
.cropper,
.flash,
.site-footer,
.form-notice,
.surface-sm,
.sheet > .desk-note,
.sheet > .desk-line,
.cal-note,
.events-rsvp-note,
.sheet > .note,
.sheet > .form-hint,
.surface-pill,
.view-switch,
.cal-nav,
.cal-back,
.cal-note,
.sheet-back,
.signin-back,
.event-more,
.events-rsvp-note,
.event-month,
.event-then-head,
.desk-head-2,
.topbar-home,
.corner-user,
.bug-button,
.tabbar{
  /* The rebind. Everything below this line in the cascade that asks for
     --bone or --chalk gets ink instead, at whatever alpha it asked for. */
  --bone:var(--ink);
  --chalk:var(--ink-soft);
  --bone-rgb:var(--ink-rgb);
  --chalk-rgb:var(--ink-soft-rgb);
  /* The one surface *inside* a card that has to read as sunk into it --
     the opened event card -- rather than as felt showing through. */
  --panel:var(--card-sunk);
  /* The other half of the domino. */
  --tile-face:#16110E;
  --tile-edge:#2E2620;
  --tile-pip:#F2EBDC;

  background:var(--card);
  color:var(--ink);
}

/* The block card. Not body.home .card -- it already sets a padding tuned
   against the tile hanging over its top edge. */
.surface,
body.bare .card,
.signin,
.sheet-head,
.cal,
.event-item,
li.event-row,
.event-empty,
.desk-card,
.legal-doc,
form.form,
.avatar-panel,
.cropper,
.site-footer{
  border:1px solid var(--card-edge);
  border-radius:14px;
  padding:1.5rem 1.6rem;
  box-shadow:var(--card-shadow);
}

/* The compact one, for a line or two of prose that still wants to wrap. */
.surface-sm,
.sheet > .desk-note,
.sheet > .desk-line,
.cal-note,
.events-rsvp-note,
.sheet > .note,
.sheet > .form-hint,
.form-notice{
  border:1px solid var(--card-edge);
  border-radius:10px;
  padding:.7rem 1rem;
  box-shadow:var(--card-shadow-sm);
}

/*
   The pill, for a single line that never wraps -- a back link, a section
   label, a set of controls. A full width card behind three words reads as
   an empty box with a caption in it; sized to its text it reads as a chip
   laid on the table, which is the same language the domino date chips and
   the view switch were already using.
*/
.surface-pill,
.view-switch,
.cal-nav,
.cal-back,
.sheet-back,
.signin-back,
.event-more,
.event-month,
.event-then-head,
.desk-head-2,
.topbar-home,
.corner-user,
.bug-button{
  width:fit-content;
  max-width:100%;
  border:1px solid var(--card-edge);
  border-radius:999px;
  padding:.4rem 1.1rem;
  box-shadow:var(--card-shadow-pill);
}

/* The centred ones keep their centring now that they are no longer full
   width; the left aligned labels must not pick it up. */
.cal-back,
.sheet-back,
.signin-back,
.event-more,
.view-switch,
.cal-nav{margin-left:auto;margin-right:auto}

/* These two are prose that wraps, so they stay full width blocks -- but
   they were centred one-liners floating on the felt and still read as an
   aside, so the card is pulled in a little rather than running the measure. */
.cal-note,
.events-rsvp-note,
.sheet > .note{
  max-width:44rem;
  margin-left:auto;
  margin-right:auto;
}

/* .note drew its own rule above itself to separate it from the felt above.
   Inside a card of its own the card's edge is the separation. */
.sheet > .note{
  margin-top:2.75rem;
  padding-top:.7rem;
  border-top:0;
}

/* A rule fading out to the right is a divider for a full width heading. On
   a pill it runs into the edge a few pixels later and reads as a scuff. */
.event-month::after,
.event-then-head::after,
.desk-head-2::after{display:none}

.event-month,
.event-then-head,
.desk-head-2{gap:0}

/* ------------------------------------------------- gaps between the cards */

/*
   Felt between the blocks rather than butted edges, which is the whole
   reason for doing this as cards instead of one sheet.
*/
.event-item,
.event-item:last-child,
li.event-row,
li.event-row:last-child{
  margin-bottom:.6rem;
  border:1px solid var(--card-edge);
  padding:0 1.1rem;
}

li.event-row,
li.event-row:last-child{padding:.85rem 1.1rem}

.desk-card{margin-bottom:.9rem}

/* ------------------------------------------------------ the opened card */

/*
   Inside its own bone item the open card is a sunk panel with the clay edge,
   not a second bordered box -- the item already draws that border, and two
   of them a pixel apart is a seam.
*/
.event-item .event-card[open]{
  margin:0 -1.1rem;
  border:0;
  border-left:3px solid var(--clay);
  border-radius:12px;
  box-shadow:none;
}
.event-item:has(> .event-card[open]){border-top-color:var(--card-edge)}
.event-item .event-card[open] .event-row{padding:.85rem 1.1rem .85rem .95rem}
.event-item .event-card[open] .event-body{padding-left:4.95rem;padding-right:1.1rem}

/* ------------------------------------------- things that must not invert */

/*
   Clay is clay on both surfaces, so its text does not follow the flip.

   The :not() is load-bearing. .button-quiet is a transparent button that
   borrows the surface behind it, so it is the one thing wearing .button
   that must invert -- without the exclusion "Cancel" came out bone on a
   bone card and vanished.
*/
.event-flag.is-live,
.view-switch .view-on,
.cal-count,
.cal-ev:hover,
.cal-ev:focus-visible,
.cal-ev:hover .cal-ev-time,
.button:not(.button-quiet),
.button:not(.button-quiet):hover{color:var(--on-clay)}

/* The domino faces and the date chips invert on their own -- they are
   drawn in --tile-* now, which the rebind above flips. Nothing to patch. */

/* --------------------------------------------- the deliberately dark ones */

/*
   A form input was a dark well on the felt. Inverted it would be a dark
   well on bone with dark text in it, so it becomes a light sunken one
   instead -- the same idea, the other way up.
*/
form.form .field input[type=text],
form.form .field input[type=email],
form.form .field input[type=tel],
form.form .field input[type=url],
form.form .field input[type=date],
form.form .field input[type=time],
form.form .field input[type=password],
form.form .field select,
form.form .field textarea,
.desk-card .desk-reject input[type=text],
li.event-row .desk-reject input[type=text]{
  background:rgb(var(--ink-rgb) / .05);
  border-color:rgb(var(--ink-rgb) / .22);
  color:var(--ink);
}

/* The bone coloured picker icon was for a dark field. On a light one the
   browser's own dark icon is the readable one. */
form.form .field input[type=date]::-webkit-calendar-picker-indicator,
form.form .field input[type=time]::-webkit-calendar-picker-indicator{filter:none}

form.form .choice-option{background:rgb(var(--ink-rgb) / .04)}
form.form .choice-option:has(input:checked){background:rgba(180,82,46,.12)}

/* Still a dark block -- it is a code block and reads as one -- so its text
   stays light rather than following the card. */
.signin .signin-code{color:var(--card)}

.legal-doc code{background:rgb(var(--ink-rgb) / .08)}

/* ------------------------------------------------------------- the chrome */

/*
   The strip across the top and the two fixed corners. Same bone chips, but
   these float over whatever is scrolling under them, so they carry a little
   more shadow and their hover states go the other way -- darker, not
   lighter, since there is no felt left to lift off.
*/
.topbar-home:hover,
.topbar-home:focus-visible,
.corner-user:hover,
.corner-user:focus-visible,
.bug-button:hover,
.bug-button:focus-visible{
  background:var(--card-sunk);
  border-color:var(--clay);
  color:var(--ink);
}

.corner-user{
  /* It was a picture with a word under it and no box; as a chip it needs
     the padding back on the sides the border now runs down. */
  padding:.4rem .45rem .5rem;
  border-radius:16px;
}
.corner-name{text-shadow:none}
.corner-avatar{box-shadow:var(--avatar-shadow)}
.corner-avatar.is-empty .avatar-bg{fill:rgb(var(--ink-rgb) / .12)}
.corner-user:hover .corner-avatar.is-empty .avatar-bg{fill:rgb(var(--ink-rgb) / .2)}

.bug-button{padding:.45rem .95rem .45rem .8rem}

/* Full window width, so no radius and no shadow -- it is a band, not a card
   laid on the table. */
.flash{
  border:0;
  border-bottom:1px solid var(--card-edge);
  box-shadow:0 10px 24px rgba(0,0,0,.3);
}

/* The banner was already a solid clay band with light text on it, which is
   the thing this whole section is asking for. It stays as it is. */

/* ------------------------------------------------------------- trimmings */

/* On bone the hard offset shadow is letterpress rather than the lift it was
   giving the type on felt, so it comes down to a hint of one. */
.sheet-title,
.wordmark{text-shadow:0 3px 0 rgb(var(--ink-rgb) / .16)}

/*
   Both notices are a wash of colour, and a wash needs something under it.
   Stated as a flat gradient over the card rather than as an rgba() on its
   own: .form-notice.is-good outranks the surface rule that sets the card
   colour, so on its own it left the felt showing through with ink on it.
*/
.form-notice{
  background:linear-gradient(rgba(180,82,46,.14), rgba(180,82,46,.14)), var(--card);
}
.form-notice.is-good{
  background:linear-gradient(rgb(var(--ink-soft-rgb) / .1), rgb(var(--ink-soft-rgb) / .1)), var(--card);
}

/* The card draws the line these were drawing for themselves. */
.form-block:last-of-type,
.newsletter-form{border-bottom:0;border-top:0;padding-top:0;margin-top:0}

.event-empty{border-style:solid}

.site-footer{margin-top:3.5rem;padding-top:1.75rem}

/* The staff mark is a tile too, so it inverts with the rest; at 28% opacity
   on bone it was disappearing altogether. */
.mark{opacity:.4}

/* ---------------------------------------------------------------- desktop */

@media (min-width: 62rem){
  /* The grid gap does the spacing between cards here, so the single column
     margin comes off. */
  body.roomy .event-list.cards .event-item,
  body.roomy .event-list.cards .event-item:last-child,
  body.home .event-list.cards .event-item,
  body.home .event-list.cards .event-item:last-child{
    margin-bottom:0;
    border:1px solid var(--card-edge);
    border-radius:14px;
    padding:0 1.1rem;
  }
  body.roomy .event-list.cards .event-card[open],
  body.home .event-list.cards .event-card[open]{margin:0 -1.1rem}
  body.roomy .event-list.cards .event-item:has(> .event-card[open]),
  body.home .event-list.cards .event-item:has(> .event-card[open]){
    border-color:var(--card-edge);
  }
}

@media (max-width: 34rem){
  /* Less of the card is padding when there is less card. */
  .surface,
  body.bare .card,
  .signin,
  .sheet-head,
  .cal,
  .desk-card,
  .legal-doc,
  form.form,
  .avatar-panel,
  .cropper,
  .site-footer{padding:1.15rem 1.1rem}

  .event-item,
  .event-item:last-child{padding:0 .8rem}
  li.event-row,
  li.event-row:last-child{padding:.85rem .8rem}
  .event-item .event-card[open]{margin:0 -.8rem}
  .event-item .event-card[open] .event-row{padding:.85rem .8rem .85rem .65rem}
  .event-item .event-card[open] .event-body{padding-left:4.5rem;padding-right:.8rem}

  .cal{padding:.8rem .6rem}
}


/* ============================================================ corner menu */
/*
   The settings panel behind the profile picture.

   A <details> whose <summary> is the chip that was already there, so the
   handle does not move or change size when the menu becomes a menu. The
   panel is one more bone card -- it is in the surface list above, which is
   what gives it the ink palette without a rule of its own.
*/

.corner-menu{position:relative}

/* The chip is a <summary> now. Both lines are needed: the second is what
   actually removes the triangle in WebKit and the first in everything
   else. */
.corner-user{list-style:none;cursor:pointer}
.corner-user::-webkit-details-marker{display:none}

/* Open, it is the thing you are using, so it takes the clay edge the rest
   of the site uses for "this one". */
.corner-menu[open] > .corner-user{
  background:var(--card-sunk);
  border-color:var(--clay);
}

.corner-panel{
  position:absolute;
  top:calc(100% + .45rem);
  right:0;
  z-index:31;
  width:15rem;
  /* Never wider than the window it is pinned inside. The corner sits .9rem
     from the right and the panel needs the same clearance on the left. */
  max-width:calc(100vw - 1.8rem);
  display:flex;
  flex-direction:column;
  gap:.1rem;
  padding:.45rem;
  border:1px solid var(--card-edge);
  border-radius:14px;
  box-shadow:var(--card-shadow);
  text-align:left;
}

.corner-who{
  padding:.35rem .7rem .5rem;
  color:var(--ink-soft);
  font-size:.78rem;
  line-height:1.35;
  overflow-wrap:anywhere;
}
.corner-who strong{color:var(--ink)}

/*
   One rule for the links and the sign out button, because they are the same
   control to the person using them and only different elements to the
   browser. A button carries none of a link's inherited type, hence the
   font:inherit.
*/
.corner-item{
  display:block;
  width:100%;
  padding:.5rem .7rem;
  border:0;
  border-radius:9px;
  background:none;
  color:var(--ink);
  font:inherit;
  font-size:.9rem;
  font-weight:600;
  line-height:1.3;
  text-align:left;
  text-decoration-color:transparent;
  cursor:pointer;
}
.corner-item:hover,
.corner-item:focus-visible{
  background:var(--card-sunk);
  color:var(--ink);
  text-decoration-color:transparent;
}
.corner-item:focus-visible{outline:2px solid var(--clay);outline-offset:-2px}

/* Signing out is the one thing in here you would not want to hit by
   accident, so it is the quietest thing in here. */
.corner-item.is-quiet{color:var(--ink-soft);font-weight:400}
.corner-item.is-quiet:hover{color:var(--ink)}

/*
   Signing in is the opposite: it is what a visitor who is not a member came
   to this menu for, and burying it in a list of three costs the site the one
   thing it wants people to do. It reads as the button it is.
*/
.corner-item.is-primary{
  margin-bottom:.15rem;
  background:var(--clay);
  color:var(--on-clay);
  text-align:center;
}
.corner-item.is-primary:hover,
.corner-item.is-primary:focus-visible{background:#c65c34;color:var(--on-clay)}

.corner-form{margin:0}

/* ------------------------------------------------------------ the toggle */

.corner-theme{
  margin-top:.3rem;
  padding:.6rem .7rem .3rem;
  border-top:1px solid var(--card-edge);
}
.corner-theme[hidden]{display:none}

.corner-theme-label{
  display:block;
  margin-bottom:.45rem;
  color:var(--ink-soft);
  font-size:.66rem;
  font-weight:600;
  letter-spacing:.16em;
  text-transform:uppercase;
}

.corner-theme-row{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:.35rem;
}

/*
   Two buttons rather than one switch. A switch has to be read twice -- once
   for the label and once to work out whether it is showing the state or the
   action -- and this says both at a glance.
*/
.theme-pick{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:.35rem;
  padding:.45rem .3rem;
  border:1px solid var(--card-edge);
  border-radius:9px;
  background:none;
  color:var(--ink-soft);
  font:inherit;
  font-size:.8rem;
  font-weight:600;
  line-height:1.2;
  cursor:pointer;
  transition:background-color 150ms ease, color 150ms ease, border-color 150ms ease;
}
.theme-pick svg{width:1rem;height:1rem;flex:0 0 auto}

.theme-pick:hover{background:var(--card-sunk);color:var(--ink)}

.theme-pick[aria-pressed="true"]{
  background:var(--clay);
  border-color:var(--clay);
  color:var(--on-clay);
}

.theme-pick:focus-visible{outline:2px solid var(--clay);outline-offset:2px}

@media (prefers-reduced-motion: reduce){
  .theme-pick{transition:none}
}

@media (max-width: 34rem){
  /* The chip itself is narrower here; the panel is not, because it holds
     the same words either way. */
  .corner-panel{width:14rem}
}


/* =================================================================== light */
/*
   The other way round, opt in from the corner menu.

   It is a small file's worth of change and not a second stylesheet, because
   the cards did the work already: after that change practically every word
   on the site is ink on a bone card, and a card looks the same in both
   themes. What is left to swap is the table it is all laid on -- the felt,
   the glows drifting over it, the grain, and the weight of the shadows the
   cards throw onto it.

   The handful of tokens that describe text sitting DIRECTLY on the felt
   (--bone, --chalk, and those two as channels) still have to flip, even
   though almost nothing uses them any more. body and a: resolve through
   them, and a stray line left bone on pale paper would be unreadable.
*/

/*
   The light palette, stated once and triggered twice.

   `:root[data-theme="light"]` is somebody who chose it from the corner menu.
   The media query is somebody who never touched it and whose system asks for
   light -- and it is written as :not([data-theme="dark"]) so that choosing
   dark explicitly still wins over the system on a light machine.

   In CSS rather than in the boot script on purpose: the site works with
   JavaScript off, and following the system preference is exactly the kind of
   thing that should not need any. The script's only job is remembering an
   explicit choice.
*/
:root[data-theme="light"]{
  /* Paper, not white. A white page under bone cards would make the cards the
     darker thing on the screen, and they are meant to be what is raised. */
  --felt:#D5CBB3;
  --felt-deep:#BFB39A;
  /* The lit centre stays a clear step below --card. At #EFE9DA it was two
     shades off the cards and they stopped reading as raised anywhere the
     radial was brightest -- which is the top of the page, where the first
     card is. */
  --felt-lift:#E7DFCB;

  --card:#FCFAF4;
  --card-edge:#D8CDB6;
  --card-sunk:#F0EBDC;

  --bone:#241D17;
  --chalk:#5B5346;
  --bone-rgb:36 29 23;
  --chalk-rgb:91 83 70;

  /* A tile is dark faced on a card in both themes; on the paper it would be
     invisible pale, so it matches. */
  --tile-face:#16110E;
  --tile-edge:#2E2620;
  --tile-pip:#F2EBDC;

  /* Warm and much closer together. The dark theme's shadows are black at a
     third; the same on paper reads as dirt rather than depth. */
  --card-shadow:0 10px 26px rgb(74 58 34 / .16);
  --card-shadow-sm:0 6px 16px rgb(74 58 34 / .13);
  --card-shadow-pill:0 4px 12px rgb(74 58 34 / .13);

  /* The lamp above the table, warmed and lifted rather than lit against the
     dark. Kept in the same alphas the dark theme uses so the drift reads at
     the same strength. */
  --glow-felt-rgb:201 188 158;
  --glow-warm-rgb:227 175 138;
  --glow-warm-deep-rgb:214 158 120;

  /* Overlay grain over a pale background darkens far more than it lightens,
     so there is less of it. */
  --grain-opacity:.07;

  /* On the felt a shadow is enough to say where the hero card ends. On paper
     it is not -- the card and the one it sits on are both --card, and a soft
     warm shadow does not draw an edge the way a dark one does -- so here it
     gets the hairline every other card already has. */
  --hero-border:1px solid var(--card-edge);
  --hero-shadow:0 14px 26px rgb(74 58 34 / .2);
  --home-shadow:0 18px 44px rgb(74 58 34 / .16);
  --chip-shadow:0 2px 6px rgb(74 58 34 / .28);
  --tile-shadow:0 12px 22px rgb(74 58 34 / .3);
  --avatar-shadow:0 2px 6px rgb(74 58 34 / .25);
  --bar-shadow:0 -6px 20px rgb(74 58 34 / .14);
}

/*
   The same values again, for a system that asks for light from somebody who
   has not chosen. There is no way to share one declaration block between two
   selectors when one of them is inside a media query, so it is stated twice
   -- which is the whole reason everything above is a token and nothing below
   this point needs to know a theme exists.
*/
@media (prefers-color-scheme: light){
  :root:not([data-theme="dark"]){
    --felt:#D5CBB3;
    --felt-deep:#BFB39A;
    --felt-lift:#E7DFCB;

    --card:#FCFAF4;
    --card-edge:#D8CDB6;
    --card-sunk:#F0EBDC;

    --bone:#241D17;
    --chalk:#5B5346;
    --bone-rgb:36 29 23;
    --chalk-rgb:91 83 70;

    --tile-face:#16110E;
    --tile-edge:#2E2620;
    --tile-pip:#F2EBDC;

    --card-shadow:0 10px 26px rgb(74 58 34 / .16);
    --card-shadow-sm:0 6px 16px rgb(74 58 34 / .13);
    --card-shadow-pill:0 4px 12px rgb(74 58 34 / .13);

    --glow-felt-rgb:201 188 158;
    --glow-warm-rgb:227 175 138;
    --glow-warm-deep-rgb:214 158 120;

    --grain-opacity:.07;
    --hero-border:1px solid var(--card-edge);
    --hero-shadow:0 14px 26px rgb(74 58 34 / .2);
    --home-shadow:0 18px 44px rgb(74 58 34 / .16);
    --chip-shadow:0 2px 6px rgb(74 58 34 / .28);
    --tile-shadow:0 12px 22px rgb(74 58 34 / .3);
    --avatar-shadow:0 2px 6px rgb(74 58 34 / .25);
    --bar-shadow:0 -6px 20px rgb(74 58 34 / .14);
  }
}


/* =============================================================== tab bar */
/*
   Three tabs along the bottom, on a phone only.

   The top of the screen was doing two jobs at once: a back-to-home link on
   the left and the account corner on the right, both of them a stretch for
   a thumb. Where you can go now lives at the bottom where the thumb already
   is, and the top keeps only who you are.

   Plain links, no script, and the current one is marked in the markup with
   aria-current -- so it survives with JavaScript off and a screen reader is
   told which tab you are on rather than being left to infer it from colour.
*/

.tabbar{display:none}

@media (max-width: 34rem){

  .tabbar{
    position:fixed;
    left:0;
    right:0;
    bottom:0;
    z-index:32;
    display:grid;
    grid-template-columns:repeat(3, 1fr);
    gap:.2rem;
    /* env() is 0 unless the viewport is ever set to viewport-fit=cover, and
       the browser insets the viewport for the home indicator itself until
       then. Written this way so turning that on later does not put the
       labels under the bar. */
    padding:.3rem .35rem calc(.3rem + env(safe-area-inset-bottom, 0px));
    border-top:1px solid var(--card-edge);
    box-shadow:var(--bar-shadow);
  }

  .tab{
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    gap:.15rem;
    /* A tap target that a thumb can hit without aiming. */
    min-height:3rem;
    padding:.35rem .2rem .3rem;
    border-radius:11px;
    color:var(--ink-soft);
    font-size:.64rem;
    font-weight:600;
    letter-spacing:.04em;
    line-height:1.2;
    text-align:center;
    text-decoration-color:transparent;
  }

  .tab svg{
    width:1.4rem;
    height:1.4rem;
    flex:0 0 auto;
  }

  .tab:hover,
  .tab:focus-visible{
    background:var(--card-sunk);
    color:var(--ink);
    text-decoration-color:transparent;
  }
  .tab:focus-visible{outline:2px solid var(--clay);outline-offset:-2px}

  /* Where you are. Colour and weight together, never colour alone. */
  .tab.is-on{color:var(--clay)}
  .tab.is-on:hover{color:var(--clay)}
  .tab.is-on .tab-label{font-weight:700}

  /* The only thing on the bar you can *do*, so it is the only solid shape
     on it. It stays clay whether or not it is the tab you are on -- a plus
     that changes colour to say "you are already here" is telling you
     something you can see from the page. */
  .tab-add .tab-plus-bg{fill:var(--clay)}
  .tab-add .tab-plus-mark{stroke:var(--on-clay);stroke-width:2.4}
  .tab-add:hover .tab-plus-bg{fill:#c65c34}

  /* Room under the last thing on the page, so the bar never sits on top of
     the footer's final line. */
  body{padding-bottom:calc(4.6rem + env(safe-area-inset-bottom, 0px))}

  /* The bug button moves up out of the bar's way. It keeps the corner it
     has always had, just higher. */
  .bug-button{bottom:calc(4.2rem + env(safe-area-inset-bottom, 0px))}

  /*
     Home is a tab now, so the link that used to carry it goes. The strip
     itself must not: .topbar's min-height is the only thing reserving room
     for the fixed .corner, and losing it drops the corner onto the first
     heading.
  */
  .topbar-home{display:none}
}

/* The bar is bone in both themes; only the weight of what it casts upward
   changes, and that is --bar-shadow. */


/* =================================================================== map */
/*
   /map: the same calendar as a set of pins, over a venue list that is the
   real page. The list is plain markup and always there; the canvas above it
   ships hidden and is only revealed once Leaflet has actually built a map in
   it, so a blocked CDN or JavaScript off costs a map and nothing else.

   Leaflet brings its own stylesheet for the tiles, controls and popup
   scaffolding. What is here is only the parts that have to look like this
   site rather than like Leaflet.
*/

.map-wrap{
  margin-bottom:1.25rem;
  padding:.5rem;
  border:1px solid var(--card-edge);
  border-radius:14px;
  background:var(--card);
  box-shadow:var(--card-shadow);
}
.map-wrap[hidden]{display:none}

.map-canvas{
  height:26rem;
  max-height:70vh;
  border-radius:10px;
  /* Leaflet paints tiles right to the edge; without this they square off the
     rounded corner the card just drew. */
  overflow:hidden;
  /* The felt shows through until the first tiles land, which is a better
     blank than white. */
  background:var(--card-sunk);
}

/* Leaflet's own popup is a white box with a hard shadow. These four make it
   the same bone card as everything else, in both themes. */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip{
  background:var(--card);
  color:var(--ink);
  box-shadow:var(--card-shadow-sm);
}
.leaflet-popup-content-wrapper{border:1px solid var(--card-edge);border-radius:12px}
.leaflet-popup-content{margin:.75rem .9rem;font-family:var(--font-body);line-height:1.5}
.leaflet-popup-content a{color:var(--clay);font-weight:600}
.leaflet-container a.leaflet-popup-close-button{color:var(--ink-soft)}

.leaflet-container{font-family:var(--font-body)}

/* The attribution is required and must stay legible, but it is not content. */
.leaflet-control-attribution{
  background:rgb(var(--bone-rgb) / .85);
  color:var(--ink-soft);
  font-size:.65rem;
}
.leaflet-control-attribution a{color:var(--ink-soft)}

.map-rough{
  display:block;
  margin:.2rem 0 .4rem;
  color:var(--clay);
  font-size:.8rem;
  font-style:normal;
}

.map-note{margin-bottom:1.5rem}

/* ------------------------------------------------------------- the places */

/*
   One card per venue. It reuses .event-item so it inherits the card surface
   and the multi-column grid the other lists get on a wide screen -- a place
   and a date are the same size of thing to look at.
*/
.place{padding:.9rem 0}

.place-name{
  font-family:var(--font-display);
  font-weight:400;
  font-size:1.15rem;
  line-height:1.2;
}

.place-where{
  margin-top:.25rem;
  color:var(--ink-soft);
  font-size:.9rem;
}

/* A town pin is not the door. It says so here as well as in the popup,
   because the list is the half that works without a map. */
.place-rough{
  margin-left:.4rem;
  padding:.05rem .4rem;
  border:1px solid rgba(180,82,46,.6);
  border-radius:3px;
  color:var(--clay);
  font-size:.62rem;
  font-weight:600;
  letter-spacing:.1em;
  text-transform:uppercase;
  white-space:nowrap;
}

.place-count{
  margin-top:.6rem;
  font-size:.9rem;
}
.place-count strong{font-family:var(--font-display);font-weight:400;font-size:1.05rem}

.place-dates{
  list-style:none;
  margin-top:.4rem;
  font-size:.9rem;
  line-height:1.7;
}

/* Every other dotted line in the file states this, because .sep carries no
   padding of its own -- it is only the zero width space and whatever the
   context gives it. Two new contexts, two more of these. */
.place-where .sep,
.place-dates .sep{padding:0 .45rem;opacity:.4}
.place-more a{color:var(--ink-soft)}

.place-go{
  margin-top:.7rem;
  padding-top:.7rem;
  border-top:1px solid rgb(var(--chalk-rgb) / .18);
  font-size:.9rem;
}

@media (max-width: 34rem){
  .map-canvas{height:20rem}
  .map-wrap{padding:.35rem}
}


/* ================================================================ import */
/*
   The CSV preview at /backstage/import. A row per line of the file, with the
   tick that decides whether it gets written, what it says, and what we think
   of it.

   Three states and they are told apart by more than colour: "New",
   "Duplicate?" and "Problem" are words, the flagged ones carry a reason in
   full, and a problem row has no checkbox at all rather than a disabled one.
*/

.import-cols{
  margin-bottom:1.1rem;
  padding:.6rem .8rem;
  border-radius:8px;
  background:rgb(var(--ink-rgb) / .05);
  font-size:.78rem;
  overflow-x:auto;
}
.import-cols code{white-space:nowrap}

.import-list{list-style:none;margin-bottom:1.25rem}

.import-row{
  display:grid;
  grid-template-columns:auto 1fr auto;
  align-items:start;
  gap:.9rem;
  padding:.8rem 0;
  border-top:1px solid rgb(var(--chalk-rgb) / .18);
}
.import-row:last-child{border-bottom:1px solid rgb(var(--chalk-rgb) / .18)}

/* A row we cannot write is dimmed, but its words stay readable -- it is the
   row you most need to read, since it is the one you have to go and fix. */
.import-row.is-bad .import-title,
.import-row.is-bad .import-where{opacity:.75}

.import-tick{
  display:flex;
  align-items:center;
  justify-content:center;
  width:1.6rem;
  padding-top:.15rem;
  cursor:pointer;
}
.import-tick input{width:1.05rem;height:1.05rem;accent-color:var(--clay)}

/* The label is for a screen reader; the column heading is the tick itself. */
.import-tick-label{
  position:absolute;
  width:1px;
  height:1px;
  overflow:hidden;
  clip-path:inset(50%);
  white-space:nowrap;
}

.import-main{
  display:flex;
  flex-direction:column;
  gap:.15rem;
  min-width:0;
}

.import-title{font-weight:600;line-height:1.35}

.import-where{
  color:var(--ink-soft);
  font-size:.9rem;
  line-height:1.45;
}
.import-where .sep{padding:0 .45rem;opacity:.4}

/* Why we flagged it, in a sentence. This is the whole point of the screen:
   a score would tell somebody nothing they could act on. */
.import-why{
  margin-top:.25rem;
  color:var(--clay);
  font-size:.85rem;
  line-height:1.45;
  text-wrap:pretty;
}
.import-row.is-bad .import-why{color:var(--ink-soft)}

.import-state{
  display:flex;
  flex-direction:column;
  align-items:flex-end;
  gap:.3rem;
  text-align:right;
}

.import-line{
  color:var(--ink-soft);
  font-size:.72rem;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
}

@media (max-width: 34rem){
  .import-row{
    grid-template-columns:auto 1fr;
    row-gap:.5rem;
  }
  /* The verdict drops under the words rather than squeezing them. */
  .import-state{
    grid-column:2;
    flex-direction:row;
    align-items:center;
    justify-content:flex-start;
    text-align:left;
  }
}
