@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/jetbrains-mono-latin-400-normal.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/jetbrains-mono-latin-400-italic.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/jetbrains-mono-latin-600-normal.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: italic;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/jetbrains-mono-latin-600-italic.woff2") format("woff2");
}

/* One ink, and everything is it: the text, the paper (the ink made pale,
   or at night dark, where the text is the ink made pale), and the tag and
   frames (a little of it on the paper). Its hue is the site's, set in the
   page (--hue, from pig.toml; olive is 125). Dark follows the system unless
   the footer toggle picked a side. */
:root {
  color-scheme: light dark;
  --ink: oklch(38% .07 var(--hue));
  /* Also worked out as hex for the theme-color metas (site.go). */
  --bg: light-dark(oklch(from var(--ink) 93% calc(c / 6) h), oklch(from var(--ink) 20% calc(c / 3) h));
  --fg: light-dark(var(--ink), oklch(from var(--ink) 88% calc(c / 2) h));
  --tag: color-mix(in oklab, var(--fg) 11%, var(--bg));
  --frame: color-mix(in oklab, var(--fg) 15%, var(--bg));
}

:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  color-scheme: dark;
}

/* The rules this sheet keeps to:
   - One font, one size, one line height, all set on html, so 1rem is the
     text size, 1rlh is one line, and 1ch is one character.
   - Everything sits on a grid of lines and characters, like a terminal:
     space comes in whole lines down the page (1rlh, 2rlh) and characters
     across it (1ch, 2ch). px is only for hairlines and outlines, % and fr
     only for columns.
   - No element sets its own outer margin. A container spaces its children
     with gap: a flex column for a stack, a grid only where columns line up.
     Things that belong closer together sit in a container of their own.
   - Type roles: regular for what's listed; italic for what a list is
     filed under, set over or beside it (a day's date, an event's time on
     its day, a month, a venue's initial or name); bold for what you land
     on (the page you're on, a heading, an event's name). Bold italic is
     only both at once, a heading that's also a key, never a role of its
     own. Then the wordmark's spacing and the "new" tag. Everything is in
     the one ink.
   - State lives in attributes (data-status, aria-current, hidden). The only
     class for looks is .bare. */

html {
  font: 100%/1.5 "JetBrains Mono", monospace;
  -webkit-text-size-adjust: 100%;
}

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

[hidden] {
  display: none !important;
}

body {
  padding: 2rlh 2ch;
  background: var(--bg);
  color: var(--fg);
}

/* The status line: pinned to the top of the window, two lines tall, over
   the column the page uses. It comes in (hidden goes) once the first day or
   venue heading scrolls up to it, and from then on names the heading you're
   under, in the heading's place. */
.statusbar {
  position: fixed;
  inset: 0 0 auto;
  padding: .5rlh 2ch;
  background: var(--bg);
}

.statusbar > div {
  max-width: 64ch;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  gap: 2ch;
}

.statusbar .where {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The page: header, list, footer, a blank line or two apart. */
main {
  max-width: 64ch;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 2rlh;
}

header {
  display: flex;
  flex-direction: column;
  gap: 1rlh;
}

/* The wordmark: spaced out. */
h1 {
  font: inherit;
  letter-spacing: .5ch;
}

/* Group headings (a venue, or one of /admin's lists): capitals, and
   italic as what the group is filed under. */
h2 {
  font: inherit;
  font-style: italic;
  text-transform: uppercase;
}

/* Every line through text is a hairline, and an underline sits just under
   the letters. */
* {
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

/* Links, and buttons that act like links: the colour around them and one
   solid underline, wherever they go. A link's words say where it goes:
   one that leaves the site is named by its address (a domain, an
   @handle), one that stays by what it opens (a venue, a date, the
   flyer). The underline is its only mark. */
a,
.textbutton,
summary {
  color: inherit;
  text-decoration-line: underline;
  cursor: pointer;
}

/* The underline answers "which of these is a link?" Ask it of the block a
   link sits in (a sentence, a row, a nav), judged by the kind of block,
   not by one instance of it. If the answer is "some", underline: the dates
   nav has days with nothing on, the views nav has the page you're on, a
   row always has text. If it's "all of them", the underline would say
   nothing, so the block is .bare and underlines only when pointed at: the
   venue jump list (its initials are labels, not items) and the wordmark. */
.bare {
  text-decoration-line: none;
}

.bare:hover,
.bare:focus-visible {
  text-decoration-line: underline;
}

.textbutton {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
}

.textbutton:disabled {
  text-decoration-line: none;
  cursor: default;
}

:focus-visible {
  outline: 1px solid var(--fg);
  outline-offset: 2px;
}

/* What a list is filed under: a date, or a time under its day. */
.date,
.time {
  font-style: italic;
}

/* Bold is for what you land on: the page you're on, then the list's
   headings and names. */
[aria-current="page"],
.day > .date,
h2,
.headline {
  font-weight: 600;
}

/* Added in the last few days: a little of the ink on the paper. */
.new {
  padding: 0 1ch;
  background: var(--tag);

}

/* The date jump list: this month and next as calendars, Sunday first,
   side by side where they fit and one over the other where they don't,
   then the later months. Every day to come shows its number, and a day
   with something on links to its first event, so the underline marks it;
   a day already past is blank. Every day takes two characters,
   right-aligned, a character apart. */
.dates {
  display: flex;
  flex-direction: column;
  gap: 1rlh;
}

.cals {
  display: flex;
  flex-wrap: wrap;
  gap: 1rlh 4ch;
}

.cal .days {
  display: grid;
  grid-template-columns: repeat(7, 2ch);
  column-gap: 1ch;
  text-align: right;
}

.month {
  font-style: italic;
}

.dates > p {
  display: flex;
  flex-wrap: wrap;
  column-gap: 2ch;
}

/* The venue jump list: an initial in the month's place, then its venues,
   wrapping under the names rather than under the initial. */
.jump > div {
  display: grid;
  grid-template-columns: 3ch 1fr;
}

.jump dt {
  font-style: italic;
}

/* The list: groups a section apart, days and the events of a day a line
   apart. */
.list {
  display: flex;
  flex-direction: column;
  gap: 2rlh;
}

.group {
  display: flex;
  flex-direction: column;
  gap: 1rlh;
}

.day {
  display: flex;
  flex-direction: column;
  gap: 1rlh;
}

/* A row: the date, then headline — venue · time, with the rest on a
   line under it. On the list by date, a day has its date over it instead,
   and each row its time where the date was, so a busy day reads like a
   schedule and a row's wrapped lines hang under its time. */
.entry {
  display: grid;
  grid-template-columns: 10ch 1fr;
  column-gap: 2ch;
}

.entry:has(> .time) {
  grid-template-columns: 7ch 1fr;
}

.entry .line,
.entry .more {
  grid-column: 2;
  min-width: 0;
  overflow-wrap: anywhere;
}

.entry .date,
.entry .time {
  white-space: nowrap;
}

/* A jump to a heading stops it where the status line's label sits, so the
   line takes it over without it moving. */
.day > .date,
.group > h2 {
  scroll-margin-top: .5rlh;
}

/* A day says its date once; later rows keep it for screen readers only. */
.entry + .entry .date {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.entry[data-status="cancelled"] .headline {
  text-decoration-line: line-through;
}

/* /admin rows act right on their second line: each action is a little form,
   kept inline so the line still reads as one. So are the edit page's two
   dates. */
.entry .more form,
.twodates form {
  display: inline;
}

/* The flyer opens in a small popup: just the flyer, matted in the page's
   paper and outlined in its ink, with the page dimmed behind it. Slides
   stack and the popup scrolls. margin: auto puts back the browser's
   centring, which the reset takes away. */
dialog {
  margin: auto;
  max-width: min(48ch, 100vw - 4ch);
  max-height: calc(100dvh - 4rlh);
  padding: 1ch;
  border: 1px solid var(--fg);
  background: var(--bg);
  color: var(--fg);
}

dialog::backdrop {
  background: color-mix(in oklab, var(--fg) 30%, transparent);
}

dialog[open] {
  display: flex;
  flex-direction: column;
  gap: 1ch;
}

/* The first slide fits the screen. */
dialog img {
  display: block;
  max-width: 100%;
  max-height: calc(100dvh - 6rlh);
  width: auto;
  height: auto;
  box-shadow: 0 0 0 1px var(--frame);
}

/* /admin: an upload or the event being edited, flyer beside the forms. */
.event {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 4fr);
  column-gap: 2ch;
}

.side,
.info {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.side {
  gap: 1rlh;
}

.info {
  gap: 1rlh;
  overflow-wrap: anywhere;
}

.flyer {
  display: block;
  align-self: start;
  position: relative;
  cursor: pointer;
}

/* Thumbnails share one shape so rows line up; the full flyer is a click away. */
.flyer img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

/* A hairline frame, drawn over the image so light flyers don't melt into the page. */
.flyer::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px var(--frame);
  pointer-events: none;
}

.slides {
  position: absolute;
  bottom: 0;
  right: 0;
  padding: 0 1ch;
  background: var(--fg);
  color: var(--bg);
}

/* No flyer posted yet: a quiet placeholder, never someone else's photo. */
.noflyer {
  aspect-ratio: 4 / 5;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 1ch;
  text-align: center;
  box-shadow: inset 0 0 0 1px var(--frame);
}

/* The edit page's flyer box: click or paste to set it. Its links stack, as
   the column is too narrow to share a line. */
.flyerlinks .textbutton,
.flyerlinks [role="status"] {
  display: block;
  text-align: left;
}

/* Forms: fields a line apart, events a section apart, and each label
   right over its field. */
.upload,
.again form {
  display: flex;
  flex-direction: column;
  gap: 1rlh;
}

.review {
  display: flex;
  flex-direction: column;
  gap: 2rlh;
}

fieldset {
  padding: 0;
  border: 0;
  min-width: 0;
}

.ev {
  display: flex;
  flex-direction: column;
  gap: 1rlh;
}

.ev > label:not(.keep),
.ev .row label {
  display: flex;
  flex-direction: column;
}

/* The edit page's kept toggle beside each label: "kept" always shows,
   "keep" only while the field is pointed at or in use (always on touch).
   It sits beside the label's text, so it's underlined like any link; the
   dot before it is set apart so the underline stays under the word. */
.ev .lock::before {
  content: "·";
  display: inline-block;
}

.ev .lock[aria-pressed="false"] {
  visibility: hidden;
}

.ev label:is(:hover, :focus-within) .lock,
.ev .lock:focus-visible {
  visibility: visible;
}

@media (hover: none) {
  .ev .lock[aria-pressed="false"] {
    visibility: visible;
  }
}

.ev .row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14ch, 1fr));
  gap: 1rlh 2ch;
}

.upload textarea,
.upload input[type="password"],
.again textarea,
.ev input:not([type="checkbox"]),
.ev select {
  display: block;
  width: 100%;
  padding: .5rlh 1ch; /* half above and half below: a field fills whole lines */
  border: 0;
  border-radius: 0;
  box-shadow: inset 0 0 0 1px var(--frame);
  background: none;
  color: inherit;
  font: inherit;
}

.upload textarea,
.again textarea {
  resize: vertical;
}

.upload input[type="file"] {
  font: inherit;
}

/* /admin/venues: one form of names, a row to a line, each a text field
   that reads as plain text until it's clicked, with the count of shows to
   its right, and Save under them all. */
.venues {
  display: flex;
  flex-direction: column;
  gap: 1rlh;
}

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

.venue {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 2ch;
}

.venue input {
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
}

.venue input:focus {
  outline: none;
  box-shadow: 0 1px 0 var(--fg);
}

/* /admin/accounts and /admin/sites: each account or site, then its latest
   post or events coming up (or what's wrong), then remove, in columns, a
   row to a line like a table; the add field under them all, as wide as
   Instagram's longest name. */
.accounts,
.sites {
  display: flex;
  flex-direction: column;
}

.account,
.site {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  column-gap: 2ch;
}

.account > span:first-child,
.site > span:first-child {
  overflow-wrap: anywhere;
}

.add input {
  width: 30ch;
  max-width: 100%;
  padding: 0;
  border: 0;
  box-shadow: 0 1px 0 var(--fg);
  background: none;
  color: inherit;
  font: inherit;
}

.add input:focus {
  outline: none;
}

/* Phones: the date sits over its row (a schedule row keeps its time
   beside it), and /admin's flyer narrows or, beside a form, goes above it
   at half width. */
@media (max-width: 480px) {
  .entry:not(:has(> .time)) {
    display: block;
  }

  .event {
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  }

  .event[data-state="review"],
  .event[data-state="stuck"] {
    grid-template-columns: 1fr;
    row-gap: 1rlh;
  }

  .event[data-state="review"] .flyer,
  .event[data-state="stuck"] .flyer {
    width: 50%;
  }
}
