/* Design tokens: see DESIGN.md. Self-hosted variable fonts, no Google Fonts
   request at runtime. */

@font-face {
  font-family: 'Archivo';
  src: url('/fonts/archivo.woff2') format('woff2-variations');
  font-weight: 400 600;
  font-display: swap;
}

@font-face {
  font-family: 'Big Shoulders Display';
  src: url('/fonts/big-shoulders-display.woff2') format('woff2-variations');
  font-weight: 700 900;
  font-display: swap;
}

@font-face {
  font-family: 'Big Shoulders Stencil';
  src: url('/fonts/big-shoulders-stencil.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

@font-face {
  /* The flyer engine's monospace face, FLYER-ENGINE-SPEC.md
     section 10. Only used inside generated flyer SVGs. */
  font-family: 'JetBrains Mono';
  src: url('/fonts/jetbrains-mono.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

:root {
  /* Colour: see DESIGN.md */
  --wall-base: #2b2a27;
  --wall-patch: #403e39;
  --paper: #d9d8d2;
  --paper-faded: #bab8b0;
  --toner: #141312;
  --stamp-ink: #8f1d1d;
  --sodium: #b8722a;
  --focus-ring: #e8b04a;
  /* Secondary text in each of the two contexts: grey on the dark wall
     (.muted, the admin's signed-in line) and softened toner on paper (a
     card's metadata). Were hex values repeated where they were used. */
  --paper-muted: #b5b3aa;
  --toner-soft: #3a3835;
  /* The one subtle divider: footer, admin tables and records, the
     calendar's feed block, the submit form's private field. */
  --border: rgba(217, 216, 210, 0.2);
  --border-strong: rgba(217, 216, 210, 0.45);
  --tape: rgba(217, 216, 210, 0.6);

  /* Type */
  --font-display: 'Big Shoulders Display', 'Arial Narrow', sans-serif;
  --font-stencil: 'Big Shoulders Stencil', 'Arial Narrow', sans-serif;
  --font-body: 'Archivo', Arial, sans-serif;
  --font-mono: 'JetBrains Mono', 'Courier New', monospace;

  --text-body: 16px;
  --text-small: 14px;
  --text-h3: 20px;
  --text-h2: 28px;
  /* The slogan strip. Capped where the slogan still fits beside the site
     name on one line inside the header's 1100px: both sizes scale with
     the viewport but their container stops growing at that point, so an
     uncapped size overflows a box that still looks half empty. */
  --text-h1: clamp(15px, 1.7vw, 23px);

  /* Shape and depth */
  --radius: 0;
  --tape-width: 28px;

  /* Motion */
  --motion-interaction: 200ms ease-out;
}

* {
  box-sizing: border-box;
}

/* Only .site-nav and .board-column ul had ever reset list styling, so every
   other <ul> on the site (past events, crews, crew profiles, harm reduction,
   the calendar's day lists) rendered with the browser's default bullets
   and 40px indent -- and, since nothing gave their <li>s any spacing
   either, adjacent event cards butted up against each other with no gap.
   Resetting this globally, with a couple of small utility classes below
   for the spacing individual lists still need, is one fix instead of
   patching each page that happens to contain a <ul>. */
ul, ol {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* The native hidden attribute is meant to always mean display: none, but
   any element that also sets its own display (grid, flex...) would
   otherwise win, since author styles beat the UA stylesheet regardless of
   specificity. This makes [hidden] reliable everywhere instead of
   special-casing it per component. */
[hidden] {
  display: none !important;
}

html {
  background: var(--wall-base) url('/textures/concrete-grain.svg') repeat;
}

body {
  margin: 0;
  background: var(--wall-base);
  background-image: url('/textures/concrete-grain.svg');
  background-repeat: repeat;
  color: var(--paper);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: 1.5;
}

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 1.25rem 3rem;
}

/* Every other page (submit, contact, crews, past events, event, admin...) is a
   single narrow column -- a form, a list, one card -- with nothing to fill
   the home page's wider 1100px, which just left it stranded against the
   left edge with a big gap on the right. The home page manages its own
   width above and is unaffected, since .page-home main is more specific. */
body:not(.page-home) main {
  max-width: 760px;
}

a {
  color: var(--paper);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Header and slogan strip, section 13.3: the one loud element.
   The band itself spans the full window, so that when it is stuck to the
   top of the viewport (below) its background covers everything scrolling
   underneath, right out to the edges. The content inside is what gets
   capped and centred.

   One width on every page, not main's narrower inner-page width: the
   header holds a wordmark, a slogan and six nav links, and squeezing
   that into the 760px reading column wraps the slogan to three lines and
   the nav to two rows. Header and content are both centred, so they
   share a centre line and read as deliberate even where the header is
   the wider of the two. */
.site-header {
  padding: 2.5rem 0 2rem;
  text-align: left;
}

.site-header-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 1.25rem;
}

.site-header .site-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--text-h2);
  margin: 0 0 0.5rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  display: inline-block;
  color: inherit;
  text-decoration: none;
  transition: transform var(--motion-interaction);
}

.site-header .site-name:hover,
.site-header .site-name:focus-visible {
  transform: rotate(-1.5deg) scale(1.04);
}

/* Site name and slogan on one line, the nav on the line below. They wrap
   to two lines only when there is genuinely no room, which on the shared
   1100px header means phones and small tablets. */
.hero-name-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
}

.hero-name-row .site-name {
  margin: 0;
  white-space: nowrap;
  font-size: clamp(28px, 4.4vw, 56px);
  /* Never shrinks: the slogan beside it takes the leftover width. */
  flex: none;
  /* inline-flex, not the inline-block set above, so the record and the
     wordmark sit on a shared centre line rather than on the text
     baseline, which would hang the disc below the letters. */
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
}

/* The record beside the wordmark. Sized in em so it tracks the site
   name's own clamp() size instead of needing its own breakpoints, and
   slightly over 1em because a circle reads smaller than the cap height
   it sits next to. The width/height attributes on the <img> give the
   browser the 1:1 ratio up front, so the header doesn't jump when the
   file lands. */
.site-name-mark {
  height: 1.08em;
  width: auto;
  flex: none;
}

.slogan-strip {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--text-h1);
  line-height: 1.15;
  margin: 0;
  background: var(--paper);
  color: var(--toner);
  display: inline-block;
  padding: 0.35em 0.5em;
  transform: rotate(-1deg);
  flex: 0 1 auto;
}

/* Below the 1024px breakpoint the name and slogan stop sharing a line
   and stack, and a left aligned wordmark over a full width slogan strip
   and a centred nav row read as three different alignments. Centring the
   row puts the name on the same centre line as the slogan and the nav
   buttons, which is what the header looks like on a phone. Only up to
   1023px: from 1024px the row is nowrap and genuinely one line, where
   flex-start is what keeps the name hard left and the slogan beside it. */
@media (max-width: 1023px) {
  .hero-name-row {
    justify-content: center;
  }
}

/* Site nav: the actionable links, up top so people don't need to scroll
   the footer to find them. Centred on the page, and wrapping, so adding
   links just fills the row and then starts a new centred one rather than
   needing the spacing retuned. */
.site-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem 1.25rem;
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
}

.site-nav a {
  display: inline-block;
  font-weight: 600;
  text-decoration: none;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--border);
}

.site-nav a:hover,
.site-nav a:focus-visible {
  border-color: var(--paper);
}

/* The page you are on. Nothing in the nav used to say where you were;
   this is the primary button's paper-and-toner treatment, so it reads as
   "selected" in the same language as everything else on the site. */
.site-nav a[aria-current="page"] {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--toner);
}

/* Paper scraps: event cards. Shape and rotation are set inline per event
   via scrapShapeFor(), see src/lib/scrapShape.js. */
.scrap {
  background: var(--paper);
  color: var(--toner);
  padding: 1rem 1.25rem;
  position: relative;
  border-radius: var(--radius);
}

/* The global `a { color: var(--paper) }` rule above is for the dark wall
   background -- on a scrap's own paper-coloured background it left every
   link (Tickets, Add to calendar, Something wrong with this listing?, Look
   after each other) the same colour as the card itself, invisible except
   when selected. .scrap-title already special-cased this; every other
   link inside a scrap needs the same fix. */
.scrap a {
  color: inherit;
}

.scrap--past {
  background: var(--paper-faded);
}

/* Capitals whatever the heading level: an event card's title is an h3 and
   picked them up from the global h3 rule, while a crew card's is an h2,
   which had none, so the crews directory was the one set of cards on the
   site with mixed-case names. */
.scrap .scrap-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-h3);
  margin: 0 0 0.4rem;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

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

/* .scrap .scrap-title is sized for a card in a list (--text-h3). The event
   page reuses the same class on its real page <h1>, where that specificity
   beat the global h1 rule and left the page's main heading smaller than
   the h1 on every other page. */
.scrap h1.scrap-title {
  font-size: var(--text-h2);
  font-weight: 800;
  text-transform: uppercase;
}

.scrap .scrap-title a:hover {
  text-decoration: underline;
}

.scrap-meta {
  font-size: var(--text-small);
  margin: 0.15rem 0;
  color: var(--toner-soft);
}

/* Free paragraphs inside a card (an event's notes): the browser's 1em
   default margin is far too much next to .scrap-meta's 0.15rem. */
.scrap-notes {
  margin: 0.75rem 0;
}

/* The event page's full lineup. A list inside a card, so tighter than
   .link-list, which is for standalone links out on the dark wall. */
.scrap-lineup {
  display: grid;
  gap: 0.1rem;
  margin: 0.5rem 0;
  font-size: var(--text-small);
}

.scrap-tape {
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%) rotate(-2deg);
  width: var(--tape-width);
  height: 18px;
  background: var(--tape);
}

/* A generated flyer's inlined SVG has no width/height attribute (only a
   viewBox), so without this it renders at the SVG default of 300x150. */
.generated-flyer svg {
  width: 100%;
  height: auto;
  display: block;
}

/* The card's title sat almost flush against the bottom of the flyer,
   closer than any two lines of text below it are to each other. */
.generated-flyer {
  margin-bottom: 0.75rem;
}

/* Status stamps: real text, not images. Section 8.2. */
.stamp {
  display: inline-block;
  font-family: var(--font-stencil);
  font-weight: 700;
  color: var(--stamp-ink);
  border: 3px solid var(--stamp-ink);
  padding: 0.15em 0.5em;
  transform: rotate(-4deg);
  text-transform: uppercase;
  font-size: var(--text-small);
}

/* Board columns, section 7.1 */
.board {
  display: grid;
  gap: 1.5rem;
}

.board-column h2 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--text-h2);
  text-transform: uppercase;
}

/* The link under the board through to /past. */
.board-more {
  margin-top: 2rem;
}

/* A vertical list of event/crew scrap cards: the board, past events, the crews directory, a crew's own upcoming/past lists. */
.board-column ul,
.card-list {
  display: grid;
  gap: 1.25rem;
}

/* One harm reduction entry: a heading, optional description and phone,
   and a "last checked" note. These used to be four bare <p>s, whose 1em
   default margins spaced the lines within an entry further apart than
   .link-list's 0.5rem gap spaced the entries themselves. */
.hr-link {
  display: grid;
  gap: 0.15rem;
  margin-bottom: 0.75rem;
}

.hr-link p {
  margin: 0;
}

.hr-link h3 {
  margin: 0;
  font-size: var(--text-h3);
  text-transform: none;
  letter-spacing: 0;
}

/* A plain list of links or short text entries with no card of their own
   (past events' year index, the calendar's per-day event lists, harm
   reduction links): needs some breathing room, just less than a card
   list's. */
.link-list {
  display: grid;
  gap: 0.5rem;
}

@media (min-width: 1024px) {
  /* The header stays put while the board scrolls under it, so the nav is
     always a click away. Deliberately not below this width: the name,
     slogan and six links stack into four or five rows on a phone, and a
     frozen block that tall would eat most of the screen.

     The band needs its own opaque background or the cards would show
     through it. The texture tiles from the header's own top left rather
     than the body's, but the grain is subtle enough that the seam is not
     visible in practice. */
  .site-header {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--wall-base);
    background-image: url('/textures/concrete-grain.svg');
    background-repeat: repeat;
  }

  /* Name and slogan on one line, which is what makes this a header rather
     than two stacked blocks. nowrap on the row is what guarantees it: the
     name is flex: none and never shrinks, so the slogan takes whatever is
     left, and wraps inside its own strip in the rare case the text is too
     long for one line.

     The strip used to carry white-space: nowrap here instead, which
     stopped it shrinking at all and so pushed the whole slogan onto a
     second row -- and it did that at exactly the wide viewports that look
     like they have room to spare, because the header is capped at main's
     1100px while both font sizes keep scaling with the window. */
  .hero-name-row {
    flex-wrap: nowrap;
  }

  .slogan-strip {
    min-width: 0;
  }

  /* The board is one column now (past events moved to /past), so on a
     wide screen its cards sit two abreast to fill the home page's 1100px
     rather than one long narrow strip. */
  .board-column ul {
    grid-template-columns: 1fr 1fr;
    align-items: start;
  }

  /* The column headings freeze directly under the frozen header, so you
     can always tell which column you are scrolling through. They need an
     opaque background of their own for the same reason the header does:
     a sticky element paints nothing but itself, so the cards would
     otherwise scroll straight over the words.

     --header-height is set by header-height.js to the header's real
     rendered height, which varies with the viewport (clamp() type sizes,
     a slogan that can wrap, a nav that rewraps), so a hardcoded offset
     would leave a gap or an overlap at every width but one. The fallback
     only applies for the moment before that script runs. z-index below
     the header's own 20, so these slide under it rather than over it if
     the two ever meet. */
  .board-column h2 {
    position: sticky;
    top: var(--header-height, 13rem);
    z-index: 10;
    margin: 0;
    padding: 0.75rem 0;
    background: var(--wall-base);
    background-image: url('/textures/concrete-grain.svg');
    background-repeat: repeat;
  }
}

/* Calendar grid, section 7.1 and 14 */
/* Capped rather than filling the column. On its own page the calendar
   has a 760px column to itself, where a full width table gives day cells
   nearly twice as wide as they are tall -- a month grid reads as a grid
   when its cells are roughly square. Left aligned, not centred, so it
   lines up with the heading and the rest of the page. */
.calendar {
  background: var(--wall-patch);
  padding: 1rem;
  max-width: 34rem;
}

.calendar table {
  width: 100%;
  /* Fixed so the 7 day columns always divide the table's own width evenly
     -- without this, the day boxes' own min-width below (set for a decent
     touch target) could force the table itself wider than its container
     on a narrow phone screen, pushing the last column or two off the
     right edge of the page (owner-reported bug). Fixed layout means a
     narrow viewport shrinks every column instead of overflowing. */
  table-layout: fixed;
  border-collapse: collapse;
}

.calendar th,
.calendar td {
  text-align: center;
  padding: 0.4rem;
  font-size: var(--text-small);
}

/* Every day gets the same size box, whether or not it links anywhere, so
   the grid reads as uniform rather than some days looking like buttons
   and others like plain text. width: 100% (not a fixed min-width) fills
   whatever the fixed table-layout above gives this column -- it can
   still shrink on a narrow screen instead of forcing the table wider
   than the page. The square aspect is what stops the cells stretching
   into letterboxes as the column gets wider, with min-height kept as a
   floor so a narrow phone still gets a 44px tap target. */
.calendar td a,
.calendar td span {
  min-height: 44px;
  aspect-ratio: 1;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.calendar td a {
  background: var(--paper);
  color: var(--toner);
  text-decoration: none;
}

/* On a phone the column is only about 350px, and the panel's own padding
   plus each cell's was eating enough of that to leave the day boxes
   33px wide against a 44px tap target. Trimmed so they come out close to
   square, which is also what the aspect-ratio above wants. */
@media (max-width: 480px) {
  .calendar {
    padding: 0.5rem;
  }

  .calendar th,
  .calendar td {
    padding: 0.2rem;
  }
}

/* Today's date, section 7.1. A paper outline rather than the sodium
   orange it used to be: DESIGN.md keeps --sodium for light on the wall
   texture, "never as a UI accent colour", and this was the one place on
   the site that did. The outline sits round the cell's padding, so it
   still shows when today is also a filled event day. */
.calendar td.is-today {
  outline: 2px solid var(--paper);
  outline-offset: -2px;
}

.calendar td.is-today span {
  font-weight: 800;
}

.calendar-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}

/* No underline: under a bare < or > it read as a less-than-or-equal
   sign. The 44px box is already the affordance, same as a day cell. */
.calendar-nav a {
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--text-h3);
  text-decoration: none;
}

.calendar-nav a:hover {
  outline: 1px solid var(--border-strong);
}

.calendar-nav-label {
  font-family: var(--font-display);
  font-weight: 800;
}

/* The per-day event lists under the calendar grid: rendered by
   calendar.js but never given any spacing, so each day's heading butted
   straight up against the previous day's last event. */
.calendar-day-lists {
  display: grid;
  gap: 1rem;
  margin-top: 1rem;
}

.calendar-day-lists h3 {
  margin: 0 0 0.35rem;
}

/* The calendar on its own page: the month grid, then the day lists the
   grid links down to. .calendar-panel, which this replaces, existed only
   to be stuck to the side of the home page board. */
.calendar-month {
  display: block;
}

/* An address meant to be read and copied (the calendar feed's URL).
   Monospace so it is obviously a thing to copy rather than prose, and
   allowed to break mid-address so a long host cannot push the page
   sideways on a phone. */
code {
  font-family: var(--font-mono, 'JetBrains Mono', 'Courier New', monospace);
  font-size: 0.9em;
  word-break: break-all;
}

/* The .ics feed, offered under the month it is a feed of rather than as
   a bare ".ics" link in the nav, which is what it used to be. */
.calendar-subscribe {
  margin-top: 2rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}

.calendar-subscribe h2 {
  margin-top: 0;
}

/* Footer, section 6 */
.site-footer {
  border-top: 1px solid var(--border);
  padding: 2rem 1.25rem;
  font-size: var(--text-small);
  display: grid;
  gap: 0.5rem;
}

.site-footer a {
  color: var(--paper);
}

/* Empty state, section 7.3 */
.empty-scrap {
  background: var(--paper);
  color: var(--toner);
  padding: 1.5rem;
  transform: rotate(-1deg);
}

/* The home page's install notice (templates/home.js, install-prompt.js):
   a scrap of paper taped to the bottom of the screen, since it has to be
   seen without shoving the board down. Paper and toner like a card, with
   the same tape strip, and no entrance animation (DESIGN.md: none on
   load). Clear of the screen edges and of an iPhone's home bar. */
.install-prompt {
  position: fixed;
  z-index: 30;
  left: 50%;
  bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%) rotate(-0.6deg);
  width: min(30rem, calc(100% - 2rem));
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  padding: 1.1rem 1.1rem 0.9rem;
  background: var(--paper);
  color: var(--toner);
  /* The one shadow on the site: it's the only thing that floats over the
     wall rather than sitting on it, and without an edge it read as part
     of whichever card was underneath. Hard and flat, not a soft blur. */
  box-shadow: 0 0 0 1px var(--toner), 4px 4px 0 rgba(0, 0, 0, 0.45);
}

/* Room under the board while the notice is up, so the last card can
   still be scrolled clear of it rather than sitting behind it for good. */
.has-install-prompt main {
  padding-bottom: 16rem;
}

.install-prompt-mark {
  width: 48px;
  height: 48px;
  flex: none;
}

.install-prompt-body {
  min-width: 0;
}

.install-prompt p {
  margin: 0 0 0.35rem;
  font-size: var(--text-small);
}

.install-prompt .install-prompt-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--text-h3);
  line-height: 1.15;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.install-prompt-share {
  vertical-align: -3px;
}

.install-prompt-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

/* On paper, so the wall's buttons are inverted: the primary is toner, the
   secondary a toner outline (the global secondary is paper-on-wall and
   would vanish here). */
.install-prompt button {
  background: var(--toner);
  border-color: var(--toner);
  color: var(--paper);
}

.install-prompt button.secondary {
  background: transparent;
  color: var(--toner);
}

@media (prefers-contrast: more) {
  .install-prompt {
    transform: translateX(-50%);
  }
}

/* Forms: submit, edit, crew and contact pages. Sections 9, 10.4, 14. */
h1 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--text-h2);
  text-transform: uppercase;
}

/* Every display-face heading is set in capitals, like the site name, the
   board's column headings and the event titles. h2 used to be the odd
   one out in mixed case, so a page went capitals, then lower case, then
   capitals again (h1, h2, h3) -- "SUBMIT AN EVENT" over "The basics", or
   "Your crew profile" over "GENERATED FLYER". Named things that must keep
   their own casing (a harm reduction service like CanTEST) opt out where
   they're rendered, see .hr-link h3. */
h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-h3);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

/* h3 had no rule at all, so the calendar's per-day headings and the crew
   dashboard's sub-headings fell back to the body font at the browser's
   default size -- a different type system to the h1/h2 sitting right
   beside them. Same family and weight as h2, one step down in size. */
h3 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-body);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.field {
  margin-bottom: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  max-width: 40rem;
}

.field label {
  font-weight: 600;
}

input,
textarea,
select {
  font: inherit;
  font-family: var(--font-body);
  padding: 0.6rem 0.75rem;
  border: 2px solid var(--paper);
  background: var(--paper);
  color: var(--toner);
  min-height: 44px;
  border-radius: var(--radius);
}

textarea {
  min-height: 100px;
}

button,
.button {
  font: inherit;
  font-family: var(--font-body);
  font-weight: 600;
  padding: 0.6rem 1.25rem;
  min-height: 44px;
  border: 2px solid var(--paper);
  background: var(--paper);
  color: var(--toner);
  cursor: pointer;
  border-radius: var(--radius);
  display: inline-block;
  text-decoration: none;
}

button.secondary,
.button.secondary {
  background: transparent;
  color: var(--paper);
}

button.danger,
.button.danger {
  background: var(--stamp-ink);
  border-color: var(--stamp-ink);
  color: var(--paper);
}

/* The submit and contact forms disable their button while a request is in
   flight, but nothing said so visually -- the button looked exactly as it
   had a moment earlier, just silently unresponsive. */
button:disabled,
.button:disabled,
button[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

.actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin: 0.75rem 0;
}

/* Step-by-step submission form, section 9.1. .form-wizard is the positioning
   context for its steps; only the active one takes up space and is
   reachable. visibility (rather than display) is deliberate: the Turnstile
   widget lives in the last step and needs real dimensions to render into
   even before that step is shown, which display: none would prevent. */
.step-progress {
  font-weight: 600;
  margin: 0 0 1rem;
}

.form-wizard {
  position: relative;
}

.form-step {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  visibility: hidden;
  pointer-events: none;
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}

.form-step.is-active {
  position: relative;
  visibility: visible;
  pointer-events: auto;
}

.form-step h2 {
  margin-top: 0;
}

.muted {
  color: var(--paper-muted);
  font-size: var(--text-small);
}

/* A field set apart from the ones above it by a rule -- the submit form's
   private contact details, which section 9.1 wants visually separate from
   the public fields. Was an inline style on that one field. */
.field--separated {
  border-top: 2px solid var(--border);
  padding-top: 1rem;
}

/* A read-only input the visitor is meant to copy out of (the one-time edit
   link on the submission confirmation page): full width, since the value
   is a long URL. Was an inline style. */
.input-full {
  width: 100%;
}

/* A generated-flyer thumbnail on a form (the crew dashboard's preview),
   as opposed to .generated-flyer, which is the full-width inlined SVG on
   a card or event page. Was an inline style. */
.flyer-preview {
  display: block;
  max-width: 300px;
  width: 100%;
  height: auto;
}

/* Visually hidden, still read out. !important because this has to win
   wherever it lands: the calendar's "(today)" note sits inside a day
   cell's <span>, and `.calendar td span` (two classes and an element)
   out-specifies a bare `.sr-only`, so it was handing this element that
   rule's display: flex, min-height: 44px and width: 100%. Absolutely
   positioned, it then stretched roughly 440px past the right edge of the
   page and put a horizontal scrollbar on the home page. */
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  min-height: 0 !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.error {
  color: var(--stamp-ink);
  background: var(--paper);
  padding: 0.75rem 1rem;
  font-weight: 600;
}

/* Same trap as .scrap a: the global `a { color: var(--paper) }` is for the
   dark wall, and on this notice's own paper background it left the link
   invisible -- the admin queue's "Review them" on the stale harm reduction
   links warning is one. Inheriting picks up the notice's stamp ink. */
.error a {
  color: inherit;
}

details.field summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--paper);
}

/* A checkbox and its label text were never actually aligned: the shared
   input/textarea/select rule above gives every <input> a 44px min-height
   (a comfortable tap target for a text field), which a checkbox also
   picked up, blowing it up to 44px square next to normal-height text.
   :has() covers every existing "<label><input type=checkbox> Text</label>"
   pair (admin forms included) without needing a class on each one; the
   .field-checkbox class is for a wrapping <div> that wants the same
   layout without relying on :has() support. */
.field label:has(> input[type="checkbox"]),
.field-checkbox label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.field label:has(> input[type="checkbox"]) input[type="checkbox"],
.field-checkbox input[type="checkbox"] {
  min-height: auto;
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  /* The browser's own margin nudged the box a few pixels in from the
     left edge every label and input above it lines up on. */
  margin: 0;
}

/* Ticked boxes were the browser's default blue, the only blue anywhere
   on the site. Paper with a toner tick instead, the same pairing as a
   primary button. */
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--paper);
}

/* DJ rows, section 9.1 rework: name, set time, genre, a headliner checkbox
   and a remove button, all in one row. This used to widen the field to
   56rem, but the row's flex bases add up to about 36rem, so it already
   fits .field's 40rem -- and at 56rem it ran 16rem past the right edge
   every other input on the form stops at. */
.field-lineup {
  max-width: 40rem;
}

/* The lineup field's group label. Not a <label> element: it labels a
   group of DJ rows, not one input, so it carries the same weight as a
   real field label without claiming to be one. */
.lineup-label {
  font-weight: 600;
}

.lineup-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.lineup-row [data-lineup-name] {
  flex: 2 1 10rem;
  min-width: 0;
}

.lineup-row [data-lineup-time],
.lineup-row [data-lineup-genre] {
  flex: 1 1 7rem;
  min-width: 0;
}

.lineup-row-headliner {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-weight: 400;
  white-space: nowrap;
}

.lineup-row-headliner input[type="checkbox"] {
  min-height: auto;
  width: 1.15rem;
  height: 1.15rem;
}

.lineup-row [data-remove-dj] {
  padding: 0.4rem 0.7rem;
  min-height: auto;
}

/* On a phone the row broke wherever the widths happened to fall: the name
   and a truncated "Set time (optio..." on one line, genre, headliner and
   remove on the next. Here it's name on its own line, then set time and
   genre side by side, then headliner and remove. */
@media (max-width: 480px) {
  .lineup-row [data-lineup-name] {
    flex-basis: 100%;
  }

  .lineup-row [data-lineup-time],
  .lineup-row [data-lineup-genre] {
    flex-basis: calc(50% - 0.25rem);
  }

  .lineup-row {
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--border);
  }
}

/* Reduced motion and high contrast, sections 13.5 and 14 */
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}

@media (prefers-contrast: more) {
  body {
    background-image: none;
  }

  .scrap,
  .empty-scrap,
  .slogan-strip {
    clip-path: none !important;
    transform: none !important;
  }
}
