/* DIYPedals.net -- "Azure Blueprint" design (see graphics/Site hierarchy redesign.zip) */

:root {
  --paper: #eef2f8;
  --grid-line: rgba(22, 35, 58, .035);
  --ink: #16233a;
  --accent: #2f6fd8;
  --accent-underline: rgba(47, 111, 216, .32);
  --accent-tick: rgba(47, 111, 216, .55);
  --muted: #8a99b3;
  --sub-text: #47546b;
  --hairline: #d5deea;
  --input-border: #ccd6e6;
  --error: #b3261e;
  --success: #1a7f4b;
}

* {
  box-sizing: border-box;
}

[x-cloak] {
  display: none !important;
}

body {
  margin: 0;
  background-color: var(--paper);
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 22px 22px;
  color: var(--ink);
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
}

/* Default: a link looks like a link (color + underline) so a plain <a>
   dropped into prose is never mistaken for inert text. Anything with its
   own visual affordance for "this is clickable" -- a nav item, a button, an
   icon+tick row -- opts out with its own `text-decoration: none` (see e.g.
   .directory-link, .side-nav-section a, a.curate-btn) instead of relying on
   links being invisible by default. */
a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--accent-underline);
  text-underline-offset: 2px;
}

a:hover {
  text-decoration-color: var(--accent);
}

input,
textarea,
button {
  font-family: inherit;
}

h1, h2, h3, h4 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 0.5em;
}

h1 { font-size: 1.75rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 20px; }
h4 { font-size: 1.05rem; }

p {
  margin: 0 0 0.75rem;
  color: var(--sub-text);
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.page {
  max-width: 820px;
  margin: 0 auto;
  padding: 56px 32px;
}

/* Header */

.site-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  border-bottom: 2px solid var(--ink);
  padding-bottom: 16px;
  margin-bottom: 14px;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 12px;
}

.wordmark {
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 600;
  font-size: 25px;
  color: var(--ink);
  text-decoration: none;
}

.wordmark:hover {
  color: var(--ink);
}

.wordmark .accent {
  color: var(--accent);
}

.meta-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--muted);
}

.header-right {
  display: flex;
  align-items: baseline;
  gap: 16px;
}

.site-nav a {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-decoration: none;
}

.login-btn {
  display: inline-block;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  border: 1px solid var(--hairline);
  padding: 7px 14px;
  transition: border-color 120ms ease, color 120ms ease;
}

.login-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Site search box (templates/shared/_site_search.html). Ships in the page
   toolbar on every page, static ones included, collapsed to a button until
   clicked -- see .header-search below. */

.header-search {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  min-width: 0;
}

/* Expanded, it grows to fill the row on wide screens; flex-wrap on
   .page-toolbar sends it to its own full-width line when the row is too
   narrow to fit alongside the breadcrumbs/density toggle. */
.header-search.is-open {
  flex: 1 1 260px;
}

.header-search > div {
  display: flex;
  width: 100%;
}

.search-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--hairline);
  background: transparent;
  color: var(--ink);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 6px 12px;
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease;
}

.search-toggle svg {
  width: 13px;
  height: 13px;
  color: var(--muted);
}

.search-toggle:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.site-search {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 0;
  width: 100%;
  border: 1px solid var(--input-border);
  background: #fff;
  transition: border-color 120ms ease;
}

.site-search:focus-within {
  border-color: var(--accent);
}

.site-search-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  color: var(--muted);
  pointer-events: none;
}

.site-search input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 12px 14px 12px 38px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  color: var(--ink);
}

.site-search input::placeholder {
  color: var(--muted);
}

.site-search input:focus {
  outline: none;
}

.site-search button {
  border: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0 26px;
  cursor: pointer;
  transition: background 120ms ease;
}

.site-search button:hover {
  background: var(--accent);
}

.search-icon {
  width: 15px;
  height: 15px;
}

/* Larger copy on the results page itself */

.site-search-page {
  margin: 0 0 28px;
}

.site-search-page input {
  padding: 14px 14px 14px 42px;
  font-size: 14px;
}

/* Page toolbar: breadcrumbs + density toggle on one line, wrapping when narrow */

.page-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 26px;
  margin-bottom: 26px;
}

.page-toolbar .breadcrumbs {
  margin: 0;
}

.page-toolbar .density-toggle {
  margin-left: auto;
}

/* Density toggle: segmented control, persisted with Alpine's $persist */

.density-toggle {
  display: inline-flex;
  border: 1px solid var(--hairline);
}

.density-option {
  display: flex;
  align-items: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 4px 14px;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}

.density-option + .density-option {
  border-left: 1px solid var(--hairline);
}

.density-option.is-active {
  background: var(--ink);
  color: var(--paper);
}

.density-option input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Work-in-progress callout (home page): dark "status panel" banner,
   deliberately the opposite of the wizard's plain white card below it.
   See templates/shared/_wip_callout.html. */

.wip-callout {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  padding: 6px 0;
  margin-bottom: 24px;
}

.wip-callout::before,
.wip-callout::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 6px;
  background: repeating-linear-gradient(
    45deg,
    var(--accent) 0, var(--accent) 10px,
    var(--paper) 10px, var(--paper) 20px
  );
}

.wip-callout::before { top: 0; }
.wip-callout::after { bottom: 0; }

.wip-inner {
  background-image:
    linear-gradient(rgba(255, 255, 255, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .05) 1px, transparent 1px);
  background-size: 22px 22px;
  padding: 20px 24px 26px;
}

.wip-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 14px;
  margin-bottom: 20px;
  border-bottom: 1px solid rgba(255, 255, 255, .15);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.wip-eyebrow {
  color: rgba(238, 242, 248, .6);
}

.wip-eyebrow-short {
  display: none;
}

.wip-status {
  color: var(--accent);
  font-weight: 600;
  white-space: nowrap;
}

.wip-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 250px;
  gap: 30px;
  align-items: start;
}

.wip-title {
  margin: 0 0 12px;
  color: var(--paper);
}

.wip-body {
  max-width: 46ch;
  color: rgba(238, 242, 248, .78);
  font-size: 14px;
  margin: 0 0 20px;
}

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

.wip-btn {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 12px 20px;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}

.wip-btn-primary {
  background: var(--accent);
  color: #ffffff;
}

.wip-btn-primary:hover {
  background: #ffffff;
  color: var(--ink);
}

.wip-btn-outline {
  border: 1px solid rgba(255, 255, 255, .35);
  color: var(--paper);
}

.wip-btn-outline:hover {
  border-color: #ffffff;
}

.wip-facts {
  margin: 0;
}

.wip-fact {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 0;
  border-top: 1px solid rgba(255, 255, 255, .15);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.wip-fact:first-child {
  border-top: none;
  padding-top: 0;
}

.wip-fact dt,
.wip-fact dd {
  margin: 0;
}

.wip-fact dt {
  color: rgba(238, 242, 248, .55);
}

.wip-fact dd {
  color: #ffffff;
  font-weight: 600;
  text-align: right;
}

.wip-fact-accent,
.wip-fact-accent a {
  color: var(--accent);
}

@media (max-width: 560px) {
  .wip-eyebrow-full {
    display: none;
  }

  .wip-eyebrow-short {
    display: inline;
  }

  .wip-status {
    display: none;
  }

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

  .wip-facts {
    display: none;
  }
}

/* Beginner wizard (home page): point-budget allocator + ranked results.
   See templates/shared/_beginner_wizard.html and js/beginner-wizard.js. */

.wizard {
  border: 1px solid var(--hairline);
  border-top: 2px solid var(--ink);
  background: #ffffff;
  padding: 20px 22px 22px;
  margin-bottom: 34px;
}

.wizard-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

.wizard-title {
  margin: 6px 0 0;
  font-size: 20px;
}

.wizard-toggle,
.wizard-reset {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--input-border);
  padding: 4px 10px;
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease;
}

.wizard-toggle:hover,
.wizard-reset:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.wizard-lede {
  margin: 14px 0 18px;
  font-size: 14px;
  max-width: 62ch;
}

/* Wide enough for the pips to sit in their own column: the running total
   reads better as a footer under them. Narrow, where each row's pips wrap
   under its label, it stays up top in DOM order (see the media query below).
   Source order is the narrow one, so it also survives with CSS off. */
@media (min-width: 561px) {
  .wizard-body {
    display: flex;
    flex-direction: column;
  }

  .wizard-lede    { order: 1; }
  .wizard-dims    { order: 2; }
  .wizard-budget  { order: 3; margin-top: 18px; }
  .wizard-actions { order: 4; }
  .wizard-results { order: 5; }
}

.wizard-budget {
  display: flex;
  align-items: baseline;
  gap: 10px;
  border-top: 1px solid var(--hairline);
  padding-top: 14px;
}

.wizard-budget-count {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 26px;
  font-weight: 600;
  color: var(--accent);
  line-height: 1;
}

.wizard-budget-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.wizard-budget .wizard-reset {
  margin-left: auto;
  align-self: center;
}

.wizard-dims {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 18px 0 0;
}

/* Column header over the pips. Dropped on narrow screens, where the meters
   wrap under their labels and there is no column left to head. */
.wizard-dims-head {
  margin-bottom: -6px;
}

.wizard-meter-head {
  /* Clears the per-row value readout, so it sits over the pips themselves. */
  margin-right: 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.wizard-dim {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 20px;
}

.wizard-dim-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.wizard-dim-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}

.wizard-dim-help {
  font-size: 13px;
  color: var(--sub-text);
}

.wizard-meter {
  display: flex;
  align-items: center;
  gap: 5px;
  flex: none;
}

/* One pip per point. Pips past what the remaining budget can buy are
   disabled rather than hidden, so the ceiling is visible while spending. */
/* Tinted rather than white: an empty box on a white card reads as a
   readout, not something to click. */
.wizard-pip {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--input-border);
  background: rgba(47, 111, 216, 0.07);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}

.wizard-pip:hover:not(:disabled) {
  border-color: var(--accent);
}

.wizard-pip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.wizard-pip.is-on {
  background: var(--accent);
  border-color: var(--accent);
}

/* Hovering a pip previews the rating that clicking it would leave behind:
   pips it would fill go pale accent, pips it would clear wash out. */
.wizard-pip.is-preview-on {
  background: rgba(47, 111, 216, 0.35);
  border-color: var(--accent);
}

.wizard-pip.is-on.is-preview-off {
  background: rgba(47, 111, 216, 0.28);
}

.wizard-pip:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

.wizard-dim-value {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--muted);
  width: 1ch;
  text-align: right;
}

.wizard-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 22px;
}

.wizard-submit {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  letter-spacing: 0.06em;
  background: var(--ink);
  color: var(--paper);
  border: none;
  padding: 11px 26px;
  cursor: pointer;
  transition: background 120ms ease;
}

.wizard-submit:hover:not(:disabled) {
  background: var(--accent);
}

.wizard-submit:disabled {
  background: var(--muted);
  cursor: not-allowed;
}

.wizard-hint {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--muted);
}

.wizard-results {
  margin-top: 24px;
  border-top: 1px solid var(--hairline);
  padding-top: 16px;
}

.wizard-results-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}

.wizard-nudge {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--muted);
}

.wizard-result {
  border-left: 2px solid var(--hairline);
  padding: 0 0 0 14px;
  margin-bottom: 18px;
}

.wizard-result.is-best {
  border-left-color: var(--accent);
}

.wizard-result-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}

.wizard-result-title {
  margin: 0;
  font-size: 15px;
}

.wizard-badge {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--paper);
  background: var(--accent);
  padding: 2px 6px;
}

.wizard-match {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--muted);
  margin-left: auto;
}

.wizard-bar {
  height: 4px;
  background: var(--hairline);
  margin: 8px 0 10px;
}

.wizard-bar span {
  display: block;
  height: 100%;
  background: var(--accent-tick);
  transition: width 180ms ease;
}

.wizard-result.is-best .wizard-bar span {
  background: var(--accent);
}

.wizard-result-blurb {
  font-size: 14px;
  margin-bottom: 8px;
}

.wizard-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--accent);
  text-decoration: none;
}

/* The best match's guide link is the page's primary call to action; the
   runners-up stay plain links. */
.wizard-cta-primary {
  background: var(--ink);
  color: var(--paper);
  padding: 10px 20px;
  margin-top: 2px;
}

.wizard-cta-primary:hover {
  background: var(--accent);
  color: var(--paper);
}

.wizard-soon {
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--muted);
  border: 1px solid var(--hairline);
  padding: 1px 5px;
}

.wizard-cta-primary .wizard-soon {
  color: rgba(238, 242, 248, 0.75);
  border-color: rgba(238, 242, 248, 0.35);
}

@media (max-width: 560px) {
  .wizard {
    padding: 16px 16px 18px;
  }

  .wizard-dims-head {
    display: none;
  }

  .wizard-dim {
    align-items: flex-start;
  }

  .wizard-meter {
    width: 100%;
  }

  .wizard-pip {
    flex: 1;
  }
}

/* Directory list (home page) */

.directory {
  display: flex;
  flex-direction: column;
}

.directory-item {
  border-top: 1px solid var(--hairline);
}

.directory-link {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 11px 2px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px;
  color: var(--ink);
  text-decoration: none;
  transition: color 120ms ease;
}

.directory-link:hover {
  color: var(--accent);
}

.directory-link .index {
  color: var(--accent);
  font-size: 12px;
  min-width: 22px;
}

.directory-link .name {
  text-decoration: underline;
  text-decoration-color: var(--accent-underline);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 4px;
}

.directory-link .arrow {
  margin-left: auto;
  color: var(--accent);
  font-size: 14px;
}

/* Sub-items: subcategories and links nested under a directory entry */

.sub-items {
  margin: -2px 0 12px 38px;
  border-left: 1px solid var(--accent-underline);
  display: flex;
  flex-direction: column;
}

.sub-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0 6px 14px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  color: var(--sub-text);
  text-decoration: none;
  transition: color 120ms ease;
}

.sub-link:hover {
  color: var(--accent);
}

.sub-link .tick {
  width: 9px;
  height: 1px;
  flex-shrink: 0;
  background: var(--accent-tick);
  display: inline-block;
}

/* Per-link-type icon (see templates/shared/_link_icon.html). Inherits the
   link's text color via currentColor, so it dims/brightens on hover. */
.link-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  opacity: 0.7;
}

.is-condensed .sub-link .link-icon {
  width: 13px;
  height: 13px;
}

/* Off-site indicator (see templates/shared/_offsite_icon.html), pinned to the
   end of the link. Everything in the links table is external. */
.offsite-icon {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  opacity: 0.5;
}

.is-condensed .sub-link .offsite-icon {
  width: 11px;
  height: 11px;
}

/* A link's own description (see DIYPLib::Category::link_summaries), shown
   as a subdued line below its row, always (including condensed mode --
   unlike .search-snippet below, which is search-specific match context and
   still hidden there). Indented to align under the row's TITLE text, not
   its tick/icon, so it reads as part of that link rather than a sibling
   entry of its own: padding-left + tick + gap + icon + gap from .sub-link's
   own box model, computed separately for the condensed variant since its
   padding/gap/icon-size all shrink. */
.link-description {
  margin: 0 0 8px 58px;
  font-size: 12.5px;
  color: var(--sub-text);
  line-height: 1.5;
}

.is-condensed .link-description {
  margin-left: 48px;
}

/* Condensed view (toggled via the header checkbox, persisted with Alpine's $persist) */

.is-condensed .directory-link {
  padding: 5px 2px;
  font-size: 14px;
  gap: 10px;
}

.is-condensed .sub-items {
  margin: -2px 0 4px 30px;
}

.is-condensed .sub-link {
  padding: 3px 0 3px 10px;
  font-size: 12px;
  gap: 8px;
}

/* Breadcrumbs */

.breadcrumbs {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin: 0 0 24px;
}

.breadcrumbs a {
  color: var(--muted);
  text-decoration: none;
}

.breadcrumbs a:hover {
  color: var(--accent);
}

/* Ancestor path shown before each entry in a category's "Related Categories"
   list (see DIYPLib::Category::related_category_summaries) -- muted, like
   .breadcrumbs, so the actual related category's title (which follows,
   unstyled) still reads as the row's real label. */
.related-crumbs {
  color: var(--muted);
}

/* Search results page (templates/search/index.html). Results reuse .sub-items /
   .sub-link so they inherit the directory's look and its condensed mode; only the
   per-result metadata below each row is new. */

.search-results > li {
  padding-bottom: 10px;
}

.search-snippet,
.search-result-context {
  margin: 0 0 0 23px;
  font-size: 12px;
  color: var(--muted);
}

.search-snippet {
  font-size: 12.5px;
  color: var(--sub-text);
  line-height: 1.5;
}

.search-snippet mark {
  background: var(--accent-underline);
  color: inherit;
  padding: 0 1px;
}

.search-result-context {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.03em;
  margin-top: 2px;
}

.search-result-context a {
  color: var(--muted);
  text-decoration: none;
}

.search-result-context a:hover {
  color: var(--accent);
}

.is-condensed .search-snippet {
  display: none;
}

.is-condensed .search-results > li {
  padding-bottom: 2px;
}

.is-condensed .search-snippet,
.is-condensed .search-result-context {
  margin-left: 19px;
}

/* Marks a section whose results came from a fallback tier (url substring or
   trigram similarity) rather than an exact word match. */
.search-approx {
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--hairline);
  padding: 2px 7px;
  margin-left: 8px;
  vertical-align: middle;
  white-space: nowrap;
}

.search-count {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin: -4px 0 10px;
}

.search-hint,
.search-empty {
  color: var(--sub-text);
  font-size: 14px;
}

.search-empty {
  color: var(--ink);
}

.search-hint code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  background: rgba(47, 111, 216, .08);
  padding: 1px 5px;
}

/* Event cards (templates/shared/_event_card.html), used by the events pages
   and the home page's teaser. Deliberately its own visual language rather
   than reusing .sub-items/.sub-link -- a list of events reads better as a
   stack of distinct appointments, each with a compact calendar-style date
   badge, than as plain directory rows. */

.event-cards {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 14px 0;
  padding: 0;
  list-style: none;
}

.event-card {
  display: flex;
  gap: 16px;
  padding: 16px;
  background: #ffffff;
  border: 1px solid var(--hairline);
  border-radius: 4px;
}

.event-card-date {
  flex: none;
  width: 68px;
  height: fit-content;
  border: 1px solid var(--hairline);
  border-radius: 4px;
  overflow: hidden;
  text-align: center;
}

.event-card-month {
  display: block;
  padding: 3px 0;
  background: var(--accent);
  color: #ffffff;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.event-card-day {
  display: block;
  padding: 4px 2px 0;
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.1;
  white-space: nowrap;
  color: var(--ink);
}

.event-card-weekday {
  display: block;
  padding: 0 2px 5px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
}

.event-card-body {
  flex: 1;
  min-width: 0;
}

.event-card-title {
  margin: 0;
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
}

/* The title itself opts out of the site-wide link look (color + underline)
   -- it needs to read as a heading first, clickable second, same reasoning
   as .curate-name/.directory-link. The off-site icon already signals that
   an event with a url leaves the site. */
.event-card-title a {
  color: inherit;
  text-decoration: none;
}

.event-card-title a:hover {
  color: var(--accent);
}

.event-card-location {
  font-weight: 400;
  color: var(--muted);
}

.event-card-dates {
  margin: 2px 0 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--sub-text);
}

.event-description {
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--sub-text);
  line-height: 1.5;
}

.event-description p {
  margin: 0 0 8px;
}

.event-description p:last-child {
  margin-bottom: 0;
}

.events-empty,
.events-invite {
  color: var(--sub-text);
  font-size: 14px;
}

.events-invite {
  margin-top: 14px;
}

.events-crosslink {
  margin-top: 20px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
}

.events-crosslink a {
  color: var(--muted);
  text-decoration: none;
}

.events-crosslink a:hover {
  color: var(--accent);
}

.pagination {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-top: 24px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
}

.pagination-status {
  color: var(--muted);
}

.pagination-disabled {
  color: var(--hairline);
}

/* -----------------------------------------------------------------------
   Shared form controls

   Every form-ish page on the site (login, recover/reset, register, the
   curate add/edit form, and the homepage "submit a link" form) is built
   from the same handful of pieces. Style them once, here, unscoped, so
   new forms get the site's look for free instead of re-declaring it
   under a new page-scoped selector:

     <div class="kicker">// SECTION</div>          -- eyebrow label
     <h1 class="page-title">Heading</h1>            -- (or h3, etc.)
     <section class="form-narrow"> ... </section>   -- narrow centered
                                                        page (skip this
                                                        wrapper class for
                                                        forms living inside
                                                        the side-nav shell,
                                                        e.g. .curate-form)
     <form> ... </form>                              -- auto vertical
                                                        layout + spacing
       <label class="field"><span>Label</span>
         <input type="text|email|password" ...>      -- or textarea/select
       </label>
       <button type="submit">GO &rarr;</button>
   ----------------------------------------------------------------------- */

.kicker {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--accent);
}

.page-title {
  margin: 6px 0 20px;
}

.form-narrow {
  max-width: 360px;
  margin: 34px auto 0;
}

form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field > span {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-transform: uppercase;
}

/* A field's optional second line of subdued explanatory text -- goes right
   after the label <span> and before the actual input/textarea/select (see
   templates/admin/users/edit.html's "Admin note" field, or
   templates/curate/pages/form.html's "Body format" field). The extra
   `.field > span.field-hint` specificity is needed to win over
   `.field > span` above, which would otherwise force it back to
   monospace/uppercase/letter-spaced like a label. */
.field > span.field-hint {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 12px;
  font-weight: 400;
  color: var(--muted);
  text-transform: none;
  letter-spacing: normal;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="url"],
textarea,
select {
  border: 1px solid var(--input-border);
  background: #ffffff;
  padding: 10px 12px;
  font-size: 14px;
  color: var(--ink);
  outline: none;
  font-family: inherit;
}

textarea {
  resize: vertical;
}

.slug-input-group {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--input-border);
  background: #ffffff;
}

.slug-prefix {
  display: flex;
  align-items: center;
  padding: 10px 0 10px 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  color: var(--muted);
  white-space: nowrap;
}

.slug-input-group input[type="text"] {
  flex: 1;
  min-width: 0;
  border: none;
  padding-left: 4px;
}

.slug-readonly {
  background: var(--paper);
}

.slug-static {
  padding: 10px 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  color: var(--ink);
}

button[type="submit"] {
  align-self: flex-start;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  letter-spacing: 0.06em;
  background: var(--ink);
  color: var(--paper);
  border: none;
  padding: 11px 26px;
  cursor: pointer;
  transition: background 120ms ease;
}

button[type="submit"]:hover {
  background: var(--accent);
}

/* Submit form */

.submit-link {
  margin-top: 34px;
  border-top: 2px solid var(--ink);
  padding-top: 22px;
}

/* Events section on the home page (templates/home.html). Same divider
   treatment as .submit-link below it, so the page reads as a stack of
   distinct sections rather than the events list blurring into the directory
   above it. */
.home-events {
  margin-top: 34px;
  border-top: 2px solid var(--ink);
  padding-top: 22px;
}

.home-events .events-empty,
.home-events .events-invite {
  margin-bottom: 0;
}

.submit-messages {
  margin-bottom: 16px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
}

.submit-messages.success {
  color: var(--success);
}

.submit-messages.errors {
  color: var(--error);
}

.submit-link .field-category {
  max-width: 280px;
}

.flash {
  margin: 0 0 16px;
  padding: 10px 14px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  border-left: 3px solid;
}

.flash-error {
  color: var(--error);
  background: rgba(179, 38, 30, 0.08);
  border-color: var(--error);
}

.flash-success {
  color: var(--success);
  background: rgba(26, 127, 75, 0.08);
  border-color: var(--success);
}

.oauth-buttons {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}

.oauth-buttons a {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid var(--input-border);
  background: #ffffff;
  padding: 10px 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--ink);
  text-decoration: none;
}

.oauth-buttons a:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.oauth-buttons svg {
  width: 16px;
  height: 16px;
  flex: none;
}

.login-page .divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 20px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-transform: uppercase;
}

.login-page .divider::before,
.login-page .divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

.account-switch {
  margin: 20px 0 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
}

.account-switch a {
  color: var(--muted);
  text-decoration: none;
}

.account-switch a:hover {
  color: var(--accent);
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.admin-table th,
.admin-table td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--hairline);
}

.admin-table th {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.admin-table td {
  color: var(--ink);
}


.admin-users-flags span {
  display: block;
}

.admin-flag {
  font-weight: 600;
  color: var(--error);
}

.admin-table tr.is-disabled {
  opacity: 0.5;
}

/* Admin: Changes (audit log / undo) */

.changes-filters {
  flex-direction: row;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 12px;
  margin: 0 0 20px;
}

.changes-filters .field {
  gap: 4px;
}

.changes-filters button[type="submit"] {
  padding: 10px 20px;
}

.changes-undo-form {
  display: flex;
  gap: 6px;
  margin: 0;
}

.changes-pager {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--muted);
}

/* Admin: Changes -- "Show details" word diff (see DIYPLib::Diff,
   DIYP::Controller::Admin::Changes' _field_diffs). A native <details> row
   under an update/restore entry, collapsed by default so the table stays
   scannable. */
.changes-diff-row td {
  padding-top: 0;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--hairline);
}

.changes-diff-row summary {
  cursor: pointer;
}

.diff-detail {
  margin: 10px 0 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 16px;
}

.diff-detail dt {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-transform: uppercase;
  white-space: nowrap;
}

.diff-detail dd {
  margin: 0;
  color: var(--ink);
}

ins.diff-add {
  color: var(--success);
  background: rgba(26, 127, 75, 0.08);
  text-decoration: none;
}

del.diff-del {
  color: var(--error);
  background: rgba(179, 38, 30, 0.08);
  text-decoration: line-through;
}

/* Curate: Submissions (dense inbox for the public "submit a link" form, see
   DIYP::Controller::Curate's submissions/_submission_row_stash and
   templates/curate/submissions.html). Extends .admin-table with tighter
   cell padding and compact in-cell form controls -- this page is meant to
   be scanned/processed row by row as fast as possible, unlike the wider
   breathing room of an add/edit form. */
.submissions-table th,
.submissions-table td {
  padding: 6px 8px;
  vertical-align: top;
}

.submissions-table input[type="text"],
.submissions-table input[type="url"],
.submissions-table select,
.submissions-table textarea {
  width: 100%;
  min-width: 9em;
  padding: 5px 6px;
  font-size: 13px;
  box-sizing: border-box;
}

.submissions-table textarea {
  min-height: 2.4em;
  resize: vertical;
}

.submissions-context {
  max-width: 22em;
  font-size: 13px;
  color: var(--muted);
}

.submissions-context a {
  color: var(--accent);
  text-decoration: none;
  word-break: break-all;
}

.submissions-comments {
  display: block;
  margin-top: 4px;
  color: var(--ink);
}

.submissions-category-hint {
  display: block;
  margin-top: 4px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.submissions-category-hint.is-unmatched {
  color: var(--error);
}

.submissions-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 7em;
}

/* Below 900px there's no honest way to fit an 8-column data-entry table
   (Received/Submitted/Title/URL/Type/Category/Description/Actions) -- each
   row becomes its own labeled card instead, stacking every field full-width
   with its column heading recreated via data-label (see submissions.html)
   since <thead> is hidden. Kept here (rather than the centralized
   "Responsive" section, where every other breakpoint lives) so these rules
   reliably win the cascade: same selectors, later in the file, same
   specificity always beats an earlier same-specificity rule regardless of
   which one is behind a media query. */
@media (max-width: 900px) {
  .submissions-table thead {
    display: none;
  }

  .submissions-table,
  .submissions-table tbody,
  .submissions-table tr,
  .submissions-table td {
    display: block;
    width: 100%;
  }

  .submissions-table tr {
    margin-bottom: 16px;
    padding: 10px;
    border: 1px solid var(--hairline);
    border-radius: 2px;
  }

  .submissions-table td {
    padding: 6px 0;
    border-bottom: none;
  }

  .submissions-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 10px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 3px;
  }

  .submissions-table input[type="text"],
  .submissions-table input[type="url"],
  .submissions-table select,
  .submissions-table textarea {
    min-width: 0;
  }

  .submissions-actions {
    flex-direction: row;
  }
}

/* Side navigation (backend only: admin/curation pages, see show_side_nav) */

.app-shell.has-side-nav {
  display: flex;
  align-items: flex-start;
  max-width: 1100px;
  margin: 0 auto;
}

.has-side-nav .page {
  flex: 1 1 auto;
  min-width: 0;
  max-width: none;
  margin: 0;
}

.side-nav {
  flex: 0 0 220px;
  position: sticky;
  top: 0;
  align-self: flex-start;
  height: 100vh;
  overflow-y: auto;
  padding: 32px 24px;
  border-right: 1px solid var(--hairline);
}

.side-nav-profile {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--hairline);
}

.side-nav-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  font-weight: 600;
}

.side-nav-identity {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.side-nav-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.side-nav-role {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--muted);
}

.side-nav-section + .side-nav-section {
  margin-top: 28px;
}

.side-nav-section h2 {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 10px;
}

.side-nav-section ul {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.side-nav-section a {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  margin: 0 -8px;
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  font-size: 14px;
  color: var(--ink);
  text-decoration: none;
  border-radius: 2px;
  transition: color 120ms ease, background 120ms ease;
}

.side-nav-section a::before {
  content: '';
  flex: none;
  width: 5px;
  height: 5px;
  background: var(--accent);
}

.side-nav-section a:hover {
  color: var(--accent);
  background: rgba(47, 111, 216, 0.06);
}

.side-nav-footer {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
}

.side-nav-footer a {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 13px;
  color: var(--muted);
  text-decoration: none;
}

.side-nav-footer a:hover {
  color: var(--accent);
}

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.nav-toggle-bar {
  display: block;
  height: 2px;
  background: var(--ink);
  border-radius: 1px;
}

/* Footer */

.site-footer {
  margin-top: 30px;
  border-top: 1px solid var(--hairline);
  padding-top: 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--muted);
}

.footer-links {
  display: flex;
  gap: 16px;
}

.footer-links a,
.footer-meta a {
  color: inherit;
  text-decoration: none;
}

.footer-links a:hover,
.footer-meta a:hover {
  color: var(--accent);
}

/* Responsive */

@media (max-width: 900px) {
  .app-shell.has-side-nav {
    display: block;
    max-width: none;
  }

  .nav-toggle {
    display: flex;
  }

  .side-nav {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    height: auto;
    width: 260px;
    max-width: 80vw;
    background: var(--paper);
    box-shadow: 1px 0 0 var(--hairline);
    transform: translateX(-100%);
    transition: transform 180ms ease;
    z-index: 101;
  }

  .side-nav.is-open {
    transform: translateX(0);
  }

  .nav-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(22, 35, 58, 0.45);
    z-index: 100;
  }

  /* Curate/Submissions toolbar (Add category/link, Pending/Handled) and
     pager (Newer/page N of M/Older): wrap instead of overflowing once the
     side-nav takeover leaves less room. */
  .curate-toolbar,
  .changes-pager {
    flex-wrap: wrap;
  }
}

@media (max-width: 480px) {
  .page {
    padding: 32px 20px;
  }

  .site-header {
    flex-direction: column;
    align-items: flex-start;
  }

  .header-right {
    width: 100%;
    flex-wrap: wrap;
    gap: 10px;
  }

  .directory-link .arrow {
    display: none;
  }

  .sub-items {
    margin-left: 20px;
  }

  .site-footer {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* Curate page */

.curate-page h2 {
  font-size: 1rem;
  margin: 24px 0 8px;
}

.curate-toolbar {
  margin: 0 0 12px;
  display: flex;
  gap: 8px;
}

/* Add/edit category form: narrower than the browse listing. Lives inside
   the side-nav shell, so (unlike .form-narrow) it doesn't get its own
   margin: auto centering -- .has-side-nav .page already handles that. */

.curate-form {
  max-width: 420px;
}

/* Add/edit category form: collapsed-by-default page/meta fields, expanded
   via native <details> (same convention as .changes-diff-row summary). */

/* Deliberately not a flex container: newer Chrome wraps everything an open
   <details> contains (other than <summary>) into a single anonymous box to
   animate open/close, so a flex `gap` here only ever sees two items --
   <summary> and that one wrapper -- and never reaches between the actual
   fields inside it. Margins on the fields themselves work the same either
   way, with or without that wrapper, so spacing here is margin-based
   throughout rather than gap-based like .field and the plain <form> above. */
.field-group-expander summary {
  cursor: pointer;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-transform: uppercase;
}

.field-group-expander[open] summary {
  margin-bottom: 16px;
}

.field-group-expander .field + .field {
  margin-top: 16px;
}

.field-checkbox {
  flex-direction: row;
  align-items: center;
}

.field-checkbox span {
  text-transform: none;
  letter-spacing: normal;
  font-family: inherit;
  font-size: 14px;
  color: var(--ink);
}

/* category_edit's related-categories checkbox tree (see
   templates/curate/category/form.html) -- a <fieldset class="field"> so its
   <legend> and <span class="field-hint"> pick up the same look as an
   ordinary field's label/hint, plus a capped, scrollable option list since
   the category tree can run to thousands of rows. */
.category-related-picker {
  border: none;
  margin: 0;
  padding: 0;
}

.category-related-picker > legend {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-transform: uppercase;
  padding: 0;
}

.category-related-picker-list {
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid var(--input-border);
  border-radius: 4px;
  padding: 8px 12px;
}

.category-related-picker-list .field-checkbox {
  margin: 4px 0;
}

.form-cancel-link {
  display: inline-block;
  margin: 20px 0 0;
  font-size: 14px;
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--accent-underline);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 4px;
}

.form-cancel-link:hover {
  text-decoration-color: var(--accent);
}

.curate-list {
  list-style: none;
  margin: 0 0 20px;
  padding: 0;
}

.curate-row {
  padding: 8px 4px;
  border-bottom: 1px solid var(--hairline);
}

.curate-row-main {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* On a small screen there's no room left for handle + icon + name + Edit/
   Delete all on one line once a title is more than a few words -- let the
   actions cluster wrap to its own line instead of squeezing the name.
   Kept next to .curate-row-main (rather than the centralized "Responsive"
   section above) so this override reliably wins the cascade over the
   plain rule it's adjusting -- same selector, later in the file, same
   specificity always beats an earlier same-specificity rule regardless of
   which one is behind a media query. */
@media (max-width: 900px) {
  .curate-row-main {
    flex-wrap: wrap;
  }
}

.curate-list.is-reordering .curate-row {
  cursor: grab;
}

.curate-row.dragging {
  opacity: 0.4;
}

.curate-handle {
  font-family: 'IBM Plex Mono', monospace;
  color: var(--muted);
  line-height: 1;
  user-select: none;
  opacity: 0.3;
}

.curate-list.is-reordering .curate-handle {
  opacity: 1;
}

.curate-name {
  flex: 1;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 14px;
  color: var(--ink);
  text-decoration: none;
}

.curate-name:hover {
  color: var(--accent);
}

/* Wraps a row's name above a line of metadata (e.g. an event's dates/
   location, templates/curate/events/index.html) instead of squeezing it into
   .curate-row-main's single flex row. Overrides .curate-name's own flex: 1
   (meant for a plain flex-row context) so it sizes to its content instead of
   stretching to fill the column alongside the meta line. */
.curate-name-group {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.curate-name-group .curate-name {
  flex: none;
}

.curate-row-meta {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--muted);
}

.curate-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

/* A Delete button is a real <form> (POST + CSRF token, see
   curate-delete.js) sitting inline among .curate-actions' <a class="curate-
   btn"> siblings -- override the site-wide `form { display: flex; ... }`
   layout rules so it doesn't stretch. The button's own look comes from the
   button[type="submit"].curate-btn rules below (shared with the admin
   Undo/Force-undo forms). */
.curate-delete-form {
  display: inline-block;
  margin: 0;
}

.curate-sublist {
  list-style: none;
  margin: 6px 0 0;
  padding: 0 0 0 26px;
}

.curate-subrow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
}

.curate-subrow.is-hidden .curate-name {
  opacity: 0.45;
}

.curate-visible {
  display: flex;
  align-items: center;
  gap: 4px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
}

.curate-visible-input {
  accent-color: var(--accent);
  cursor: pointer;
}

.curate-visible-input:disabled {
  cursor: wait;
}

.curate-btn {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--input-border);
  border-radius: 2px;
  padding: 4px 8px;
  cursor: not-allowed;
}

/* Links (Add / Edit) are functional, unlike the disabled placeholder
   buttons (Delete), so they get the usual pointer and no underline. */
a.curate-btn {
  display: inline-block;
  text-decoration: none;
  cursor: pointer;
}

.curate-btn-danger {
  color: var(--error);
}

/* A real <button type="submit"> using .curate-btn (the curate Delete forms,
   admin Undo/Force-undo forms) ties with button[type="submit"]'s
   specificity on the class/attribute tier (1 vs 1) for every property they
   both set, and loses each tie because that rule also has an element
   selector (button) -- so button[type="submit"]'s dark "SAVE" look wins
   instead of the outlined .curate-btn look, with the text color
   specifically ending up invisible until :hover (where a same-issue :hover
   rule happened to have enough specificity to win instead). Re-assert
   every shared property at a specificity that beats button[type="submit"]
   outright, regardless of which form wraps the button. */
button[type="submit"].curate-btn {
  align-self: auto;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--input-border);
  border-radius: 2px;
  padding: 4px 8px;
  cursor: pointer;
}

button[type="submit"].curate-btn:hover {
  background: transparent;
}

button[type="submit"].curate-btn-danger {
  color: var(--error);
}

button[type="submit"].curate-btn-danger:hover {
  color: var(--error);
  border-color: var(--error);
}

.curate-reorder-toggle {
  cursor: pointer;
}

.curate-reorder-toggle[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
}

/* Submissions' Pending/Handled filter toggle -- same "currently selected"
   treatment as the reorder toggle above. */
a.curate-btn.is-active {
  color: var(--accent);
  border-color: var(--accent);
}

/* Static CMS pages (templates/page/show.html) -- long-form prose rendered
   from a curator's Markdown or raw HTML body, so it needs its own basic
   typesetting for tags the rest of the site never produces. */

.static-page-body h2 { margin-top: 1.5em; }
.static-page-body h3 { margin-top: 1.25em; }

.static-page-body ul {
  list-style: disc;
  margin: 0 0 0.75rem;
  padding-left: 1.5em;
}

.static-page-body ol {
  list-style: decimal;
  margin: 0 0 0.75rem;
  padding-left: 1.5em;
}

.static-page-body li {
  color: var(--sub-text);
  margin-bottom: 0.25em;
}

.static-page-body blockquote {
  margin: 0 0 0.75rem;
  padding: 0.25em 1em;
  border-left: 3px solid var(--accent-underline);
  color: var(--sub-text);
}

.static-page-body code {
  font-family: 'IBM Plex Mono', monospace;
  background: var(--grid-line);
  padding: 0.1em 0.3em;
  border-radius: 3px;
}

.static-page-body pre {
  background: var(--grid-line);
  padding: 12px 16px;
  border-radius: 6px;
  overflow-x: auto;
}

.static-page-body pre code {
  background: none;
  padding: 0;
}

.static-page-body table {
  border-collapse: collapse;
  margin: 0 0 0.75rem;
  width: 100%;
}

.static-page-body th,
.static-page-body td {
  border: 1px solid var(--hairline);
  padding: 6px 10px;
  text-align: left;
}

.static-page-body img {
  max-width: 100%;
  height: auto;
}
