/* =====================================================================
   TEMPLE - page-scoped partial, same role as apply.css.
   Loads after the site partials (base…apply) and before fallbacks/mobile,
   so it only ADDS temple components: tokens, fonts, reset, nav, hero,
   banner, buttons and footer all come from the shared partials. Keep it
   that way - anything generic belongs in the site partials, not here.
   ===================================================================== */

/* the one token the site palette doesn't carry: an error tone
   (matches the apply form's #f0857b) */
:root {
  --danger: #f0857b;
  --danger-rgb: 240, 133, 123;
}

/* The auth-dependent UI toggles the `hidden` attribute, and several
   temple rules set an explicit `display` - which outranks the UA
   stylesheet's [hidden] handling and would show admin-only items to
   everyone (cosmetically only; RLS is the real gate - but still). */
.temple [hidden],
.lp-app [hidden],
.lp-auth [hidden],
.lp-auth-body [hidden] {
  display: none !important;
}

/* ------------------------------------------------ temple hero (home)
   The about page's hero shell (pages.css owns the sizing, bottom fade
   and the Learn more pin); this variant left-aligns the copy. The
   butterfly flock was retired at launch. */
.lp-butterfly-hero .lp-hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 1180px;
}
.lp-hero-chip {
  display: inline-block;
  margin-bottom: 22px;
  padding: 6px 12px;
  border: 1px solid rgba(var(--signal-rgb), 0.4);
  background: rgba(var(--signal-rgb), 0.08);
  color: var(--signal-soft);
}
.lp-butterfly-hero .lp-hero-title {
  text-align: left;
  font-size: clamp(2.4rem, 4.6vw, 4.9rem);
  line-height: 1.12;
}
.lp-hero-copy {
  position: relative;
  z-index: 2;
  max-width: 44ch;
  margin-top: 22px;
  color: rgba(255, 255, 255, 0.78);
  font-size: 16px;
}
.lp-hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 34px;
}
@media (max-width: 860px) {
  .lp-butterfly-hero .lp-hero-title {
    font-size: clamp(2.1rem, 9vw, 2.9rem);
  }
}

/* ------------------------------------------------ extra button variants */
.btn-signal {
  background: var(--signal);
  color: #fff;
}
.btn-signal:hover {
  background: var(--signal-soft);
}
.btn-sm {
  padding: 9px 16px;
  font-size: 11px;
}
.btn[disabled],
.btn.is-busy {
  opacity: 0.45;
  cursor: not-allowed;
}
/* destructive actions: red, and they answer only to a long press -
   see LP.holdButton in lp.js. The fill sweep duration must match
   HOLD_MS there (1200ms). */
.btn-danger {
  border: 1px solid rgba(var(--danger-rgb), 0.55);
  background: rgba(var(--danger-rgb), 0.07);
  color: var(--danger);
}
.btn-danger:hover {
  border-color: var(--danger);
  background: rgba(var(--danger-rgb), 0.14);
  color: var(--danger);
}
.btn-hold {
  position: relative;
  overflow: hidden;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  touch-action: none;
}
.btn-hold .hold-fill {
  position: absolute;
  inset: 0;
  background: rgba(var(--danger-rgb), 0.38);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.16s ease-out;
  pointer-events: none;
}
/* the sweep is progress feedback for a safety mechanism, so it stays
   under reduced motion too - without it a held button looks dead */
.btn-hold.is-holding .hold-fill {
  transform: scaleX(1);
  transition: transform 1.2s linear;
}
.btn-hold .hold-label {
  position: relative;
}
/* the "hold to confirm" prompt lives OUTSIDE the button, floated just
   above it - the label under the finger stays the action's own name for
   the whole press. Absolute, so showing it never nudges the row. */
.hold-wrap {
  position: relative;
  display: inline-flex;
}
.hold-hint {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  z-index: 2;
  /* wraps rather than runs off a phone: the Finnish string is nearly
     twice the width of the button it sits over */
  width: max-content;
  max-width: 11rem;
  text-align: center;
  line-height: 1.4;
  /* moderation rows are divided by hairlines and the hint lands right on
     them - the panel ground behind the text cuts the rule instead of
     letting it strike through */
  padding: 0 7px;
  background: var(--surface);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--txt-dim);
  pointer-events: none;
  opacity: 0;
  transform: translateX(-50%) translateY(3px);
  transition:
    opacity 0.18s ease,
    transform 0.18s ease;
}
.hold-wrap.is-hinting .hold-hint {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
.btn-danger + .hold-hint {
  color: var(--danger);
}
@media (prefers-reduced-motion: reduce) {
  .hold-hint {
    transition: none;
    transform: translateX(-50%);
  }
}
/* holding is also how entries get SENT - on the orange submit button the
   sweep is a white wash, not the danger tone */
.btn-signal.btn-hold .hold-fill {
  background: rgba(255, 255, 255, 0.3);
}
/* the review pane parks its delete on the left, like the quiet buttons
   (hold buttons carry the flag on their wrapper — that is the flex item) */
.temple .form-actions .btn-danger,
.temple .form-actions .hold-wrap.is-danger {
  margin-right: auto;
}

.btn-quiet {
  padding: 6px 0;
  border: 0;
  background: none;
  color: var(--txt-dim);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.btn-quiet:hover {
  color: var(--txt);
}
/* ghost buttons sit on flat dark panels here, not imagery - raise contrast
   (same correction apply.css makes for its form nav) */
.temple .panel .btn-ghost,
.temple .gate .btn-ghost {
  border-color: rgba(255, 255, 255, 0.5);
  color: var(--txt);
}
.temple .panel .btn-ghost:hover,
.temple .gate .btn-ghost:hover {
  border-color: #fff;
  color: #fff;
}

/* ================================================ app shell
   Connext-style top bar: brand on the left, the platform menu as pills
   in the middle, theme flip + bell + avatar on the right. App pages
   carry no site nav and no app.js - this shell is the chrome. */
.lp-app {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
}
.lp-side-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
}
.lp-side-brand img {
  height: 19px;
  width: auto;
}
.lp-side-pad {
  font-size: 9px;
  letter-spacing: 0.2em;
  color: var(--signal-soft);
  border: 1px solid rgba(var(--signal-rgb), 0.45);
  padding: 2px 6px;
  white-space: nowrap;
}
.lp-topnav {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.lp-topnav-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 20px;
  max-width: 1200px;
  margin: 0 auto;
  padding: 12px 24px;
}
.lp-nav-pills {
  display: flex;
  align-items: center;
  gap: 4px;
  /* Basis 0, not auto. An auto basis is the strip's own content width, so
     a menu wider than the room left beside the brand pushed itself onto a
     second line instead of scrolling: that is what the six-item admin set
     did at 1024px, and what the Finnish labels would do further up still.
     At 0 the strip never asks for room it has not got, and overflow-x
     takes it from there, so the header is one row at every desktop width
     in every language. */
  flex: 1 1 0;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.lp-nav-pills::-webkit-scrollbar {
  display: none;
}
.lp-nav-pills a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  color: var(--txt-soft);
  text-decoration: none;
  font-size: 14px;
  white-space: nowrap;
  transition:
    color 0.2s ease,
    background 0.2s ease;
}
/* lucide stroke icons (vendored inline from Iconify) */
.lp-ico {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  opacity: 0.72;
  transition: opacity 0.2s ease;
}
.lp-nav-pills a:hover {
  color: var(--txt);
  background: var(--surface-2);
}
.lp-nav-pills a:hover .lp-ico {
  opacity: 1;
}
/* the current page is an inverted pill, like the reference's Home */
.lp-nav-pills a[aria-current="page"] {
  background: var(--txt);
  color: var(--bg);
}
.lp-nav-pills a[aria-current="page"] .lp-ico {
  opacity: 1;
}
.lp-topnav-right {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-left: auto;
}
a.lp-avatar {
  text-decoration: none;
  transition: border-color 0.2s ease;
}
a.lp-avatar:hover {
  border-color: var(--signal);
}
.lp-content {
  flex: 1;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 44px 24px 72px;
}
@media (max-width: 720px) {
  /* The header is the brand and the controls at this size - section 4
     moves the menu out of it and onto a bar at the bottom of the screen.
     One row, but it only stays one row if the brand, the controls and the
     gap between them fit the viewport, and at the original sizes they did
     not: 24px of padding, a 20px gap, a 201px brand and 128px of controls
     comes to 397px, which a 393px phone misses by four pixels. The
     controls took a line of their own, and nothing caught it because a
     wrap is neither an overflow nor an undersized target.

     Four pixels, so the lockup gives them back rather than the layout.
     18px is the padding .lp-content already uses at this breakpoint, and
     the smaller logo and badge bring the row to about 306px against
     360px, enough slack that the mono badge measuring differently once
     IBM Plex lands cannot push it over. */
  .lp-topnav-row {
    gap: 10px 12px;
    padding: 12px 18px;
  }
  .lp-side-brand {
    gap: 8px;
  }
  .lp-side-brand img {
    height: 16px;
  }
  .lp-side-pad {
    font-size: 8px;
    letter-spacing: 0.16em;
    padding: 2px 5px;
  }
  .lp-content {
    padding: 28px 18px 56px;
  }
}

/* Under 360px the badge is the piece that stops row one fitting, and it is
   also the piece the page can spare: the pill strip directly beneath it
   already names the section, and the logo still carries the brand. Without
   it the logo and controls come to about 252px, which holds down to the
   240px fold cover the rest of this file is built for. */
@media (max-width: 359.98px) {
  .lp-side-pad {
    display: none;
  }
  /* At 240px - the narrowest this file is built for - the logo and the
     three controls still come to 252px against 18px padding and a 12px
     gap, so row one wanted a third line for the sake of twelve pixels.
     Both numbers come down; nothing here is measured in text, so unlike
     the badge above these widths do not move when a font lands. */
  .lp-topnav-row {
    padding: 12px 14px;
  }
  .lp-topnav-right {
    gap: 8px;
  }
}
.lp-page-head {
  margin-bottom: 30px;
}
.lp-page-head-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.lp-page-head-row .btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.lp-page-kicker {
  color: var(--txt-dim);
  margin-bottom: 10px;
}
.lp-page-head h1 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.7rem, 3vw, 2.4rem);
  letter-spacing: -0.02em;
  line-height: 1.15;
}
.lp-lede {
  color: var(--txt-soft);
  max-width: 62ch;
  margin: -14px 0 22px;
}

.lp-bell {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  background: none;
  border: 1px solid var(--line);
  color: var(--txt-dim);
  cursor: pointer;
  transition:
    color 0.2s ease,
    border-color 0.2s ease;
}
.lp-bell:hover {
  color: var(--txt-soft);
  border-color: var(--txt-dim);
}
.lp-bell svg {
  width: 17px;
  height: 17px;
}
.lp-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt-soft);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
}

/* back link in the auth top bar */
.lp-topbar-back {
  color: var(--txt-dim);
  text-decoration: none;
}
.lp-topbar-back:hover {
  color: var(--txt);
}

/* ------------------------------------------------ auth card (login) */
.lp-auth-body {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  position: relative;
  isolation: isolate;
  overflow-x: hidden;
}
/* Dithered backdrop: the site's paper shader running a slow
   simplex field in a muted signal tone, fixed behind the forms. The
   shader hides its canvas without WebGL2, revealing the gradient washes
   below as the designed fallback. */
.lp-auth-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}
.lp-auth-bg .dither-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* the field should murmur, not shout */
  opacity: 0.3;
}
/* scrim: quiet the field where the form sits, keep it lively at the
   edges - same move as the site heroes */
.lp-auth-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 62% 70% at 50% 34%,
    rgba(var(--bg-rgb), 0.82),
    rgba(var(--bg-rgb), 0.42) 58%,
    rgba(var(--bg-rgb), 0.12) 100%
  );
}
/* Fallback (and reduced-motion base) layer: two oversized radial washes
   drifting slowly (transform-only - the compositor does all the work).
   Static under reduced motion. Only visible when the canvas is not. */
.lp-auth-body::before,
.lp-auth-body::after {
  content: "";
  position: fixed;
  z-index: -2;
  width: 90vmax;
  height: 90vmax;
  border-radius: 50%;
  pointer-events: none;
}
.lp-auth-body::before {
  top: -35vmax;
  left: -25vmax;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(var(--signal-rgb), 0.2),
    rgba(var(--signal-rgb), 0.07) 42%,
    transparent 68%
  );
}
.lp-auth-body::after {
  bottom: -40vmax;
  right: -30vmax;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(var(--signal-soft-rgb), 0.14),
    rgba(var(--signal-rgb), 0.05) 45%,
    transparent 70%
  );
}
@media (prefers-reduced-motion: no-preference) {
  .lp-auth-body::before {
    animation: lp-aurora-a 26s ease-in-out infinite alternate;
  }
  .lp-auth-body::after {
    animation: lp-aurora-b 34s ease-in-out infinite alternate;
  }
}
@keyframes lp-aurora-a {
  from {
    transform: translate3d(0, 0, 0) scale(1);
  }
  to {
    transform: translate3d(16vmax, 10vmax, 0) scale(1.18);
  }
}
@keyframes lp-aurora-b {
  from {
    transform: translate3d(0, 0, 0) scale(1.12);
  }
  to {
    transform: translate3d(-14vmax, -8vmax, 0) scale(0.94);
  }
}
/* auth pages wear the same top bar as the app, as glass: it stays put
   and blurs whatever drifts underneath (shader, washes, the form) */
.lp-auth-body .lp-topnav {
  background: rgba(var(--bg-rgb), 0.35);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  backdrop-filter: blur(14px) saturate(1.2);
  border-bottom-color: rgba(255, 255, 255, 0.08);
}
html[data-lp-theme="light"] body.lp-auth-body .lp-topnav {
  border-bottom-color: rgba(0, 0, 0, 0.08);
}
.lp-auth {
  flex: 1;
  display: grid;
  place-items: start center;
  padding: 48px 20px 80px;
}
/* Flat, like the apply form body - the .application class (apply.css)
   brings the volunteer field system: inputs, focus rings, .field-error
   inline validation and the custom .consent-box checkboxes. */
.auth-card {
  width: 100%;
  max-width: 560px;
}
.auth-tabs {
  margin-bottom: 26px;
}
.auth-card .field-hint,
.auth-pw-hint {
  font-size: 12.5px;
  color: var(--txt-dim);
}
.auth-pw-hint {
  margin: -8px 0 20px;
}
.auth-card .consent-field {
  margin-bottom: 14px;
}
/* Shared with the entry form's own submission Turnstile widget - not
   scoped to .auth-card, since both live under .temple. */
.turnstile-check {
  margin: 4px 0 20px;
}
.turnstile-widget {
  min-height: 65px;
}
.turnstile-error {
  margin: 8px 0 0;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--danger);
}
/* date of birth: three memorable-date fields instead of a native date
   picker (whose popup calendar is unstylable browser chrome and a chore
   for a birth date anyway) */
.dob-field {
  border: 0;
  padding: 0;
  margin: 0 0 20px;
}
.dob-row {
  display: flex;
  gap: 14px;
}
/* preferred widths, but shrinkable - at 320px viewports the fixed trio
   (84+84+112 + gaps) would otherwise push the card into page overflow */
.dob-part {
  flex: 0 1 84px;
  min-width: 0;
}
.dob-part-year {
  flex: 0 1 112px;
  min-width: 0;
}
.dob-part-label {
  display: block;
  margin-bottom: 6px;
  font-size: 12px;
  color: var(--txt-dim);
}
.dob-part input {
  text-align: center;
}
/* apply.css tunes its ghost/back buttons for the form body; the quiet
   text buttons in these form-navs need their spacing kept */
.auth-card .form-nav .btn-quiet {
  margin-right: auto;
}
.auth-title {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.5rem;
  letter-spacing: -0.01em;
  margin-bottom: 18px;
}
.auth-opt {
  color: var(--txt-dim);
  text-transform: none;
  letter-spacing: 0;
}
/* (consent checkboxes come from apply.css - the volunteer form's
   .consent / .consent-box system) */
.auth-card .consent-text a {
  color: var(--signal-soft);
}

/* ------------------------------------------------ countdown chip */
.count-chip {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 26px;
  padding: 8px 14px;
  border: 1px solid rgba(var(--signal-rgb), 0.35);
  background: rgba(var(--signal-rgb), 0.08);
  color: var(--signal-soft);
  font-size: 11px;
}
.count-chip .count-chip-time {
  font-size: 14px;
  letter-spacing: 0.06em;
  color: var(--txt);
  font-variant-numeric: tabular-nums;
}
.count-chip.is-over {
  color: var(--txt-dim);
  border-color: var(--line);
  background: none;
}
.card-count {
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------ live field errors
   (LP.liveValidate) - red message under the control, red border on it */
.temple .lp-field-error {
  margin-top: 7px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--danger);
}
.temple .field input.is-invalid,
.temple .field textarea.is-invalid,
.temple .set-control input.is-invalid,
.temple .set-control textarea.is-invalid {
  border-color: var(--danger);
}
.temple .field input.is-invalid:focus,
.temple .field textarea.is-invalid:focus,
.temple .set-control input.is-invalid:focus,
.temple .set-control textarea.is-invalid:focus {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px rgba(var(--danger-rgb), 0.16);
}

/* ------------------------------------------------ async states / notices */
.loading,
.empty {
  padding: 28px 0;
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
}
.notice {
  margin: 0 0 28px;
  padding: 12px 14px;
  border: 1px solid rgba(var(--signal-rgb), 0.32);
  background: rgba(var(--signal-rgb), 0.1);
  color: var(--signal-soft);
  font-size: 13.5px;
  line-height: 1.5;
}
.notice.is-error {
  border-color: rgba(var(--danger-rgb), 0.4);
  background: rgba(var(--danger-rgb), 0.08);
  color: var(--danger);
}
/* the home page parks its notice container between hero and sections -
   give it breathing room only when a notice is actually in it */
.temple-home [data-lp-notice] .notice {
  margin: 28px 0 0;
}

/* ------------------------------------------------ home sections */
.lp-how {
  padding: 72px 0;
  border-bottom: 1px solid var(--line);
}
.lp-how ol {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: 28px;
  counter-reset: how;
  margin: 0;
  padding: 0;
}
@media (min-width: 768px) {
  .lp-how ol {
    grid-template-columns: repeat(3, 1fr);
  }
}
.lp-how li {
  counter-increment: how;
  border-top: 1px solid var(--line);
  padding-top: 18px;
}
.lp-how li::before {
  content: counter(how, decimal-leading-zero);
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--signal-soft);
  margin-bottom: 12px;
}
.lp-how h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.15rem;
  margin-bottom: 8px;
}
.lp-how p {
  color: var(--txt-soft);
  font-size: 14px;
}
.lp-section {
  padding: 72px 0;
}
.lp-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 30px;
}
.lp-section-head h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.4rem, 3vw, 2rem);
  letter-spacing: -0.02em;
}
.lp-section-head a {
  color: var(--txt-dim);
  text-decoration: none;
  white-space: nowrap;
}
.lp-section-head a:hover {
  color: var(--txt);
}
.lp-section-head p {
  color: var(--txt-dim);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: right;
}

/* ------------------------------------------------ challenge cards
   One problem per row: title + status on the first line, a plain
   summary, and one quiet mono meta line. */
.challenge-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
}
.challenge-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 28px 30px;
  background: var(--surface);
  border: 1px solid var(--line);
  text-decoration: none;
  transition:
    border-color 0.25s ease,
    background 0.25s ease,
    transform 0.25s var(--ease);
}
.challenge-card:hover {
  border-color: rgba(var(--signal-rgb), 0.55);
  background: var(--surface-2);
}
@media (prefers-reduced-motion: no-preference) {
  .challenge-card:hover {
    transform: translateY(-2px);
  }
}
.challenge-card .card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px 20px;
}
.challenge-card h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.25rem, 2.4vw, 1.5rem);
  letter-spacing: -0.015em;
  line-height: 1.2;
}
.challenge-card .card-head .chip {
  margin-top: 5px;
}
.challenge-card .card-summary {
  color: var(--txt-soft);
  font-size: 15px;
  max-width: 70ch;
}
.challenge-card .card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  margin-top: 6px;
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.challenge-card .card-count {
  color: var(--signal-soft);
}

/* the landing renders cards as buttons (they open the detail modal) -
   same skin as the anchor cards, plus button resets */
button.challenge-card {
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

/* ------------------------------------------------ challenge modal (landing)
   Fixed backdrop dims and blurs the page; the dialog is a flat panel in
   the site's surface tones. Scroll stays inside the dialog. */
html.lp-modal-open {
  overflow: hidden;
}
.lp-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 22px;
  background: rgba(8, 8, 10, 0.62);
  -webkit-backdrop-filter: blur(9px);
  backdrop-filter: blur(9px);
}
.lp-modal {
  position: relative;
  width: min(680px, 100%);
  /* --svh, not raw vh: on iOS vh is the bar-HIDDEN height, so 86vh can be
     taller than what the reader can actually see and the modal's last rows
     sit under the browser bar. base.css declares --svh (1svh where
     supported, 1vh as the fallback) and it is loaded on every temple
     page, so this works here without app.js pinning it. */
  max-height: min(700px, calc(var(--svh) * 86));
  overflow-y: auto;
  padding: 34px 36px 30px;
  background: var(--surface);
  border: 1px solid var(--line);
}
@media (prefers-reduced-motion: no-preference) {
  .lp-modal-backdrop {
    animation: lp-modal-fade 0.18s ease-out;
  }
  .lp-modal {
    animation: lp-modal-rise 0.24s var(--ease);
  }
}
@keyframes lp-modal-fade {
  from {
    opacity: 0;
  }
}
@keyframes lp-modal-rise {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}
.lp-modal-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  background: none;
  border: 1px solid var(--line);
  color: var(--txt-dim);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  transition:
    color 0.2s ease,
    border-color 0.2s ease;
}
.lp-modal-close:hover {
  color: var(--txt);
  border-color: var(--txt-dim);
}
.lp-modal-close:active {
  transform: scale(0.95);
}
@media (prefers-reduced-motion: reduce) {
  .lp-modal-close:active {
    transform: none;
  }
}
.lp-modal-head {
  display: flex;
  align-items: flex-start;
  gap: 10px 18px;
  padding-right: 44px;
}
.lp-modal-head h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.3rem, 3vw, 1.65rem);
  letter-spacing: -0.015em;
  line-height: 1.2;
}
.lp-modal-head .chip {
  margin-top: 6px;
}
.lp-modal-summary {
  margin-top: 12px;
  color: var(--txt-soft);
  font-size: 15px;
  max-width: 62ch;
}
.lp-modal-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  margin: 20px 0;
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.lp-modal-meta .card-count {
  color: var(--signal-soft);
  font-variant-numeric: tabular-nums;
}
.lp-modal-brief {
  padding-top: 20px;
  border-top: 1px solid var(--line);
  color: var(--txt-soft);
  font-size: 14.5px;
  white-space: pre-line;
  max-width: 68ch;
}
.lp-modal-ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 14px;
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
@media (max-width: 560px) {
  .lp-modal {
    padding: 26px 20px 22px;
  }
}

/* ------------------------------------------------ bug-report widget
   A small always-there escape hatch while the platform ships fast: a
   circular link to a prefilled mailto, fixed bottom-left on every
   Temple page. Hidden while a modal is open so it never sits over
   the backdrop. */
.lp-bugwidget {
  position: fixed;
  left: 18px;
  bottom: 18px;
  z-index: 60;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--txt-soft);
  font-size: 18px;
  line-height: 1;
  text-decoration: none;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.28);
  transition:
    color 0.2s ease,
    border-color 0.2s ease,
    transform 0.2s ease;
}
.lp-bugwidget:hover {
  color: var(--txt);
  border-color: var(--txt-dim);
}
.lp-bugwidget:active {
  transform: scale(0.95);
}
@media (prefers-reduced-motion: reduce) {
  .lp-bugwidget:active {
    transform: none;
  }
}
html.lp-modal-open .lp-bugwidget {
  display: none;
}
@media (max-width: 560px) {
  .lp-bugwidget {
    left: 14px;
    bottom: 14px;
  }
}

/* ------------------------------------------------ topic filter (board)
   A fixed set of pills above the challenge grid, one active at a time.
   Purely client-side: the board's challenges are already fetched, a
   pill click just re-filters what is already in memory. Individually
   bordered rather than one fused segmented control (see .seg) because
   category names run long and the row needs to wrap freely on a phone. */
.lp-cat-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: -6px 0 22px;
}
.lp-cat-filter button {
  padding: 7px 14px;
  border: 1px solid var(--line);
  background: none;
  color: var(--txt-soft);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  cursor: pointer;
  transition:
    background 0.2s ease,
    border-color 0.2s ease,
    color 0.2s ease;
}
.lp-cat-filter button:hover {
  border-color: var(--txt-dim);
  color: var(--txt);
}
.lp-cat-filter button.is-active {
  background: var(--signal);
  border-color: var(--signal);
  color: #fff;
}

/* status + kind chips */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 3px 10px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border: 1px solid var(--line);
  color: var(--txt-dim);
  white-space: nowrap;
}
.chip::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
}
.chip-open {
  color: var(--signal-soft);
  border-color: rgba(var(--signal-rgb), 0.45);
}
.chip-closed {
  color: var(--txt-dim);
}
.chip-draft,
.chip-archived {
  color: var(--grey);
  border-style: dashed;
}
.chip-submitted {
  color: var(--txt-soft);
}
.chip-under_review {
  color: var(--signal-soft);
  border-color: rgba(var(--signal-rgb), 0.45);
}
.chip-accepted {
  color: #9ce0b9;
  border-color: rgba(156, 224, 185, 0.45);
}
.chip-rejected {
  color: var(--danger);
  border-color: rgba(var(--danger-rgb), 0.4);
}

/* ------------------------------------------------ challenge detail */
/* prize band - the one thing on this page that gets to look like a
   prize rather than a metadata line. Hairlines rather than a tinted
   box: this page is built from rules, and a filled panel read as
   bolted on. Each prize captions itself ("1st prize"), so there is no
   separate "Prizes" heading saying the same thing a line above and
   leaving an empty row behind. Shares the 760px column the brief and
   the entry panel keep. */
.challenge-prize-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px 36px;
  max-width: 760px;
  margin: 0 0 22px;
  padding: 14px 0;
  border-top: 1px solid rgba(var(--signal-rgb), 0.35);
  border-bottom: 1px solid rgba(var(--signal-rgb), 0.35);
}
.prize-banner-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.prize-banner-rank {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--signal-soft);
}
.prize-banner-value {
  font-size: 19px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--txt);
}
.challenge-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 22px;
  margin-bottom: 40px;
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* with the prizes lifted into their own banner, the points value is the
   thing left in this row that people actually scan for - it gets the
   brighter text the prizes used to carry here */
.challenge-meta-row .meta-points {
  color: var(--txt);
  font-size: 12px;
}
.challenge-brief {
  max-width: 72ch;
  color: var(--txt-soft);
  white-space: pre-line;
  margin-bottom: 56px;
}

/* ------------------------------------------------ panels + forms */
.panel {
  max-width: 760px;
  padding: 26px;
  background: var(--surface);
  border: 1px solid var(--line);
}
.panel + .panel {
  margin-top: 18px;
}
.panel h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.3rem;
  letter-spacing: -0.01em;
  margin-bottom: 6px;
}
.panel .panel-sub {
  color: var(--txt-dim);
  font-size: 13.5px;
  margin-bottom: 20px;
}
.temple .field {
  margin-bottom: 20px;
}
.temple .field > label,
.temple .field-label {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--txt-dim);
  margin-bottom: 9px;
}
.temple .field input[type="text"],
.temple .field input[type="url"],
.temple .field input[type="number"],
.temple .field input[type="password"],
.temple .field input[type="date"],
.temple .field input[type="datetime-local"],
.temple .field textarea,
.temple .field select,
/* The auth card is not inside .temple (it is .lp-auth-body > .auth-card),
   and apply.css - which dresses its inputs - has no select rule, so the
   referral dropdown rendered as a raw native control among dark fields. */
.lp-auth-body .field select {
  width: 100%;
  /* background-color, not the `background` shorthand: the shorthand resets
     background-image, and this rule outranks the `.temple select` one that
     draws the dropdown arrow - so selects lost their arrow entirely while
     appearance:none had already removed the native one. */
  background-color: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.5;
  padding: 13px 15px;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}
.temple .field textarea {
  resize: vertical;
  min-height: 130px;
}
.temple .field input::placeholder,
.temple .field textarea::placeholder {
  color: var(--txt-dim);
}
.temple .field input:focus,
.temple .field textarea:focus,
.temple .field select:focus,
.lp-auth-body .field select:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px rgba(var(--signal-rgb), 0.16);
}

/* The volunteer recipe (apply.css) styles text/email/url/number inputs -
   the platform's auth forms also use password and date controls, which
   would otherwise render as bare native fields. Same recipe, same focus
   ring, same invalid state. */
.application input[type="password"],
.application input[type="date"] {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--txt);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.5;
  padding: 13px 15px;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}
.application input[type="password"]:focus,
.application input[type="date"]:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px rgba(var(--signal-rgb), 0.16);
}
.application input[type="password"].is-invalid,
.application input[type="date"].is-invalid {
  border-color: #d9544a;
}
.application input[type="password"].is-invalid:focus,
.application input[type="date"].is-invalid:focus {
  border-color: #d9544a;
  box-shadow: 0 0 0 3px rgba(217, 84, 74, 0.18);
}
.temple .field .field-hint {
  margin-top: 7px;
  font-size: 12.5px;
  color: var(--txt-dim);
}
.temple .field-row {
  display: grid;
  gap: 14px 18px;
  grid-template-columns: 1fr;
}
@media (min-width: 560px) {
  .temple .field-row {
    grid-template-columns: 1fr 1fr;
  }
}
.temple .field-checks {
  display: flex;
  gap: 22px;
  align-items: center;
}
.temple .field-checks label {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  font-size: 14px;
  color: var(--txt-soft);
  cursor: pointer;
}
.temple .field-checks input {
  accent-color: var(--signal);
  width: 16px;
  height: 16px;
}
.kind-toggle {
  display: inline-flex;
  border: 1px solid var(--line);
  margin-bottom: 20px;
}
.kind-toggle button {
  padding: 9px 18px;
  background: none;
  border: 0;
  color: var(--txt-dim);
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transition:
    background 0.2s ease,
    color 0.2s ease;
}
.kind-toggle button.is-active {
  background: var(--signal);
  color: #fff;
}
.temple .form-error {
  margin: 16px 0 0;
  padding: 12px 14px;
  border: 1px solid rgba(var(--danger-rgb), 0.4);
  background: rgba(var(--danger-rgb), 0.08);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--danger);
}
.temple .form-ok {
  margin: 16px 0 0;
  padding: 12px 14px;
  border: 1px solid rgba(var(--signal-rgb), 0.32);
  background: rgba(var(--signal-rgb), 0.1);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--signal-soft);
}
.temple .form-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.temple .form-actions .btn-quiet {
  margin-right: auto;
}

/* ------------------------------------------------ own-entry view */
.submission-view .sub-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 18px;
  margin-bottom: 16px;
}
/* What the member wrote is read-only, so it must not wear the input
   recipe (filled box + full border) that the editable fields below it
   use - a reviewer should be able to tell at a glance which single
   control on this panel is actually typable. A left rule reads as
   quoted content instead. */
.submission-view .sub-body {
  padding: 10px 0 10px 14px;
  background: none;
  border: 0;
  border-left: 2px solid var(--line);
  white-space: pre-line;
  overflow-wrap: anywhere;
  color: var(--txt-soft);
  font-size: 14px;
}
.submission-view .sub-body + .sub-body {
  margin-top: 10px;
}
/* the suggestion's topic is data, not a field label - without this it
   renders in the same mono/uppercase/dim as the labels below it and
   reads as the caption for the box underneath */
.submission-view .sub-cat {
  display: inline-block;
  margin-bottom: 4px;
  padding: 4px 9px;
  border: 1px solid rgba(var(--signal-rgb), 0.3);
  background: rgba(var(--signal-rgb), 0.07);
  color: var(--signal-soft);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
/* the read-only stack above carries no bottom margin of its own, which
   left the first field label crowding the content it follows */
.submission-view form {
  margin-top: 24px;
}
.submission-view .sub-body a {
  color: var(--signal-soft);
  overflow-wrap: anywhere;
}
.submission-view .sub-score {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--signal-soft);
}
.submission-view .sub-feedback {
  margin-top: 16px;
  padding: 16px;
  border-left: 2px solid var(--signal);
  background: rgba(var(--signal-rgb), 0.06);
  color: var(--txt-soft);
  font-size: 14px;
  white-space: pre-line;
}
.submission-view .sub-feedback .field-label {
  margin-bottom: 6px;
}

/* ------------------------------------------------ leaderboard */
.board {
  max-width: 760px;
}
.board-row {
  display: grid;
  grid-template-columns: 48px 34px 1fr auto auto;
  gap: 16px;
  align-items: center;
  padding: 15px 18px;
  border: 1px solid var(--line);
  border-top: 0;
  background: var(--surface);
}
.board-row:first-child {
  border-top: 1px solid var(--line);
}
.board-row .b-rank {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.08em;
  color: var(--txt-dim);
}
.board-row .b-name {
  font-weight: 500;
  overflow-wrap: anywhere;
}
.board-row .b-entries {
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.board-row .b-points {
  font-family: var(--mono);
  font-size: 14px;
  color: var(--signal-soft);
}
.board-row.is-top {
  border-left: 2px solid var(--signal);
}
.board-row.is-top .b-rank {
  color: var(--signal-soft);
}
.board-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt-soft);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.03em;
}
@media (max-width: 560px) {
  .board-row {
    grid-template-columns: 34px 30px 1fr auto;
  }
  .board-row .b-entries {
    display: none;
  }
}
.board-note {
  margin-top: 18px;
  color: var(--txt-dim);
  font-size: 13px;
  max-width: 56ch;
}

/* ------------------------------------------------ account */
.account-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  align-items: start;
}
@media (min-width: 900px) {
  .account-grid {
    grid-template-columns: 340px 1fr;
  }
}
.stat-row {
  display: flex;
  gap: 28px;
  margin: 4px 0 18px;
}
.stat .stat-n {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.9rem;
  letter-spacing: -0.02em;
}
.stat .stat-l {
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Left-aligned, matching .lp-page-head above it - centering this as
   its own island read as disconnected from the heading, which stays
   pinned left. Same 760px as .panel so the list below it lines up. */
.suggest-page {
  max-width: 760px;
}
.suggest-list-wrap {
  margin-top: 40px;
}
.suggest-list-wrap h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.3rem;
  letter-spacing: -0.01em;
  margin-bottom: 14px;
}

/* ------------------------------------------------ entry rows (account + admin) */
.sub-list {
  display: flex;
  flex-direction: column;
}
.sub-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
  padding: 15px 18px;
  border: 1px solid var(--line);
  border-top: 0;
  background: var(--surface);
  text-align: left;
  width: 100%;
  font: inherit;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition:
    background 0.2s ease,
    transform 0.2s var(--ease);
}
.sub-row:first-child {
  border-top: 1px solid var(--line);
}
.sub-row:hover {
  background: var(--surface-2);
}
.sub-row .sr-title {
  font-weight: 500;
  flex: 1 1 220px;
}
.sub-row .sr-who {
  color: var(--txt-soft);
  font-size: 13.5px;
}
.sub-row .sr-date {
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
}
.sub-row .sr-score {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--signal-soft);
}
.sub-row .sr-slug {
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 11px;
}
div.sub-row {
  cursor: default;
}

/* ------------------------------------------------ the word vote
   Community > the yrittäjä word vote. A ranked list of candidate words,
   each with an up/down arrow pair and a score. The arrows are the only
   controls, so they carry the state: orange when you have upvoted, the
   error tone when you have downvoted, both from the shared tokens. */
.wv-page {
  display: grid;
  gap: 22px;
  /* the same column the panels sit in, so "Your words" lines up with
     them instead of running the full content width */
  max-width: 760px;
}
.wv-question {
  margin: 0 0 8px;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.45rem;
  letter-spacing: -0.01em;
}
.wv-closed {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--danger);
}
.wv-board {
  margin-top: 20px;
}
.wv-row {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 16px;
  align-items: start;
  padding: 16px 0;
  border-top: 1px solid var(--line);
}
.wv-row:first-child {
  border-top: 0;
  padding-top: 4px;
}
.wv-vote {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}
.wv-arrow {
  display: grid;
  place-items: center;
  width: 34px;
  height: 25px;
  padding: 0;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--txt-dim);
  font-size: 10px;
  line-height: 1;
  cursor: pointer;
  transition:
    color 0.15s ease,
    border-color 0.15s ease,
    background 0.15s ease;
}
.wv-arrow:hover:not(:disabled) {
  color: var(--txt);
  border-color: var(--txt-dim);
}
.wv-arrow:disabled {
  opacity: 0.45;
  cursor: default;
}
.wv-arrow.is-up {
  color: var(--signal);
  border-color: rgba(var(--signal-rgb), 0.5);
  background: rgba(var(--signal-rgb), 0.1);
}
.wv-arrow.is-down {
  color: var(--danger);
  border-color: rgba(var(--danger-rgb), 0.5);
  background: rgba(var(--danger-rgb), 0.1);
}
.wv-score {
  width: 34px;
  text-align: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--txt-soft);
}
.wv-score.is-plus {
  color: var(--signal);
}
.wv-score.is-minus {
  color: var(--danger);
}
.wv-word {
  margin: 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
}
.wv-note {
  margin: 7px 0 0;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--txt-soft);
}
.wv-tally {
  margin: 9px 0 0;
  font-size: 11.5px;
  letter-spacing: 0.04em;
  color: var(--txt-dim);
}
.wv-add h2,
.wv-mine-wrap h2 {
  margin: 0 0 6px;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
}
.wv-mine-wrap h2 {
  margin-bottom: 14px;
}
@media (prefers-reduced-motion: reduce) {
  .wv-arrow {
    transition: none;
  }
}

/* ------------------------------------------------ admin */
.admin-tabs {
  display: inline-flex;
  border: 1px solid var(--line);
  margin-bottom: 34px;
}
.admin-tabs button {
  padding: 10px 22px;
  background: none;
  border: 0;
  color: var(--txt-dim);
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.admin-tabs button.is-active {
  background: var(--signal);
  color: #fff;
}
.admin-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 22px;
}
.admin-filters select,
.admin-filters input[type="search"] {
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--txt);
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  padding: 9px 12px;
}
.admin-filters input[type="search"] {
  flex: 1 1 160px;
  max-width: 240px;
}
.admin-filters input[type="search"]::placeholder {
  color: var(--txt-dim);
}
.admin-filters input[type="search"]:focus {
  outline: none;
  border-color: var(--signal);
}
.admin-filters .btn {
  margin-left: auto;
}
.admin-quick {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 14px 0 6px;
}
.admin-quick-hint {
  margin-bottom: 18px;
  font-size: 12.5px;
  color: var(--txt-dim);
}
/* capped to the same 760px as the .panel it wraps, so the rp-head's
   right-aligned byline lands over the panel edge instead of stranding
   itself at the far side of a full-width row */
.review-pane {
  max-width: 760px;
  margin-top: 26px;
}

/* ---- challenge board: previous / current / upcoming ---- */
.admin-board {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  align-items: start;
}
@media (min-width: 900px) {
  .admin-board {
    grid-template-columns: repeat(3, 1fr);
  }
}
.ab-col {
  border: 1px solid var(--line);
  background: var(--surface);
  padding: 16px;
}
.ab-col-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--txt-dim);
}
.ab-col-head span {
  color: var(--signal-soft);
}
.ab-card {
  display: block;
  width: 100%;
  padding: 14px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition:
    border-color 0.2s ease,
    background 0.2s ease;
}
.ab-card + .ab-card {
  margin-top: 10px;
}
.ab-card:hover {
  border-color: rgba(var(--signal-rgb), 0.55);
}
.ab-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px 14px;
}
.ab-title {
  font-weight: 500;
  font-size: 14.5px;
  line-height: 1.35;
}
.ab-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin-top: 10px;
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.ab-meta .ab-sched {
  color: var(--signal-soft);
}
.ab-empty {
  color: var(--txt-dim);
  font-size: 13px;
  padding: 8px 0;
}

/* ---- challenge timeline ---- */
.gantt {
  position: relative;
  border: 1px solid var(--line);
  background: var(--surface);
  padding: 14px 16px 18px;
  overflow: hidden;
}
.gantt-months {
  position: relative;
  height: 22px;
  margin-left: 220px;
  border-bottom: 1px solid var(--line);
}
.gantt-month {
  position: absolute;
  top: 0;
  padding-left: 6px;
  border-left: 1px solid var(--line);
  /* The rules run from the month strip down through every row, and .gantt
     clips whatever is left over. The length must therefore beat the
     timeline's own height - which grows with the challenge count and has
     nothing to do with the viewport. 200vh looked like plenty until a
     window cropped to 360px tall left a 28-row timeline 273px short of
     rules, so this is deliberately a flat, unreachable length instead. */
  height: 9999px;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--txt-dim);
  pointer-events: none;
}
.gantt-row {
  display: flex;
  align-items: stretch;
}
.gantt-label {
  width: 220px;
  flex: 0 0 220px;
  padding: 9px 14px 9px 0;
  border: 0;
  background: none;
  text-align: left;
  font-family: var(--body);
  font-size: 13.5px;
  color: var(--txt-soft);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gantt-label:hover {
  color: var(--txt);
}
.gantt-track {
  position: relative;
  flex: 1;
  min-height: 34px;
}
.gantt-bar {
  position: absolute;
  top: 7px;
  bottom: 7px;
  border: 1px solid var(--signal);
  background: rgba(var(--signal-rgb), 0.3);
  cursor: pointer;
  padding: 0;
}
.gantt-bar:hover {
  background: rgba(var(--signal-rgb), 0.5);
}
.gantt-bar.is-draft {
  background: transparent;
  border-style: dashed;
  border-color: var(--signal-soft);
}
.gantt-bar.is-closed,
.gantt-bar.is-archived {
  border-color: var(--txt-dim);
  background: rgba(255, 255, 255, 0.06);
}
.gantt-bar.is-archived {
  border-style: dashed;
}
html[data-lp-theme="light"] body.lp-app-body .gantt-bar.is-closed,
html[data-lp-theme="light"] body.lp-app-body .gantt-bar.is-archived {
  background: rgba(0, 0, 0, 0.06);
}
.gantt-today {
  position: absolute;
  top: 10px;
  bottom: 10px;
  width: 0;
  border-left: 2px solid var(--danger);
  pointer-events: none;
}

/* ---- custom date+time picker (challenge editor) ---- */
.dtp {
  position: relative;
}
.dtp-input {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 13px 15px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt);
  font-family: var(--body);
  font-size: 14.5px;
  text-align: left;
  cursor: pointer;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}
.dtp-input:hover {
  border-color: var(--txt-dim);
}
.dtp-input:focus,
.dtp-input[aria-expanded="true"] {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px rgba(var(--signal-rgb), 0.16);
}
.dtp-value.dtp-placeholder {
  color: var(--txt-dim);
}
.dtp-caret {
  color: var(--txt-dim);
  font-size: 10px;
}
.dtp-pop {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 70;
  width: min(300px, 90vw);
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.42);
}
.dtp-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}
.dtp-title {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--txt);
}
.dtp-nav {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  background: none;
  border: 1px solid var(--line);
  color: var(--txt-dim);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  transition:
    color 0.2s ease,
    border-color 0.2s ease;
}
.dtp-nav:hover {
  color: var(--txt);
  border-color: var(--txt-dim);
}
.dtp-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.dtp-dow {
  padding: 4px 0;
  text-align: center;
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--txt-dim);
}
.dtp-day {
  aspect-ratio: 1;
  border: 1px solid transparent;
  background: none;
  color: var(--txt-soft);
  font-family: var(--mono);
  font-size: 12.5px;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition:
    background 0.15s ease,
    color 0.15s ease;
}
.dtp-day:hover {
  background: var(--surface-2);
  color: var(--txt);
}
.dtp-day.is-out {
  opacity: 0.35;
}
.dtp-day.is-today {
  border-color: rgba(var(--signal-rgb), 0.55);
}
.dtp-day.is-sel {
  background: var(--signal);
  color: #fff;
  border-color: var(--signal);
}
.dtp-time {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
.dtp-at {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--txt-dim);
}
.dtp-time select {
  flex: 1;
  padding: 8px 10px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt);
  font-family: var(--mono);
  font-size: 12.5px;
}
.dtp-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

/* ---- members ---- */
.mem-dot {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt-soft);
  font-family: var(--mono);
  font-size: 10px;
}
.mem-info {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: 10px;
  margin-bottom: 18px;
}
.mem-info > div {
  border: 1px solid var(--line);
  background: var(--surface-2);
  padding: 10px 12px;
}
.mem-k {
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--txt-dim);
  margin-bottom: 5px;
}
.mem-v {
  font-size: 14px;
  color: var(--txt);
  overflow-wrap: anywhere;
}
.temple .panel .field-label {
  margin: 18px 0 10px;
}

/* moderation rows in the member detail */
.mod-row {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.mod-row .field-hint {
  flex: 1 1 240px;
  font-size: 12.5px;
  color: var(--txt-dim);
}
.review-pane .rp-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
}
.review-pane .rp-head h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.2rem;
}

/* ------------------------------------------------ signed-out gate */
.gate {
  max-width: 560px;
  padding: 40px 32px;
  border: 1px solid var(--line);
  background: var(--surface);
  text-align: center;
}
.gate h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.4rem;
  margin-bottom: 10px;
}
.gate p {
  color: var(--txt-soft);
  margin-bottom: 24px;
}

/* ================================================ platform theming
   The orange accent now comes from the site tokens in base.css, so the
   platform only adds its LIGHT theme: html[data-lp-theme="light"] (set
   before paint by the pinned head snippet, flipped live from
   Settings/topbar) relights the neutrals and deepens the orange so it
   holds up on paper. Scope stays the app shell (body.lp-app-body) and
   the auth pages (body.lp-auth-body); the rest of stuhi.org is dark. */
html[data-lp-theme="light"] body.lp-app-body,
html[data-lp-theme="light"] body.lp-auth-body {
  color-scheme: light;
  --bg: #f6f6f7;
  --bg-rgb: 246, 246, 247;
  --surface: #ffffff;
  --surface-2: #f2f2f4;
  --line: #e4e4e8;
  --txt: #18181b;
  --txt-rgb: 24, 24, 27;
  --txt-soft: #47474e;
  --txt-dim: #74747c;
  /* darker orange pair - the dark theme's tints wash out on white */
  --signal: #ea580c;
  --signal-rgb: 234, 88, 12;
  --signal-soft: #c2410c;
  --signal-soft-rgb: 194, 65, 12;
}
/* the solid/ghost button recipes assume dark ground - invert on light */
html[data-lp-theme="light"] body.lp-app-body .btn-solid,
html[data-lp-theme="light"] body.lp-auth-body .btn-solid {
  background: var(--ink);
  color: #fff;
}
html[data-lp-theme="light"] body.lp-app-body .btn-solid:hover,
html[data-lp-theme="light"] body.lp-auth-body .btn-solid:hover {
  background: var(--ink-soft);
}
html[data-lp-theme="light"] body.lp-app-body .btn-ghost,
html[data-lp-theme="light"] body.lp-auth-body .btn-ghost,
html[data-lp-theme="light"] .lp-app.temple .panel .btn-ghost,
html[data-lp-theme="light"] .lp-app.temple .gate .btn-ghost {
  border-color: rgba(0, 0, 0, 0.35);
  color: var(--txt);
}
html[data-lp-theme="light"] body.lp-app-body .btn-ghost:hover,
html[data-lp-theme="light"] body.lp-auth-body .btn-ghost:hover {
  border-color: var(--ink);
  color: var(--ink);
}
/* green "accepted" reads pale on white */
html[data-lp-theme="light"] body.lp-app-body .chip-accepted {
  color: #1f7a45;
  border-color: rgba(31, 122, 69, 0.45);
}
/* the auth backdrop shader paints a dark field - light mode drops the
   canvas and keeps the (now orange) gradient washes on paper */
html[data-lp-theme="light"] .lp-auth-bg {
  display: none;
}
/* the brand mark is a white PNG - flip it to ink on light ground */
html[data-lp-theme="light"] body.lp-app-body .lp-side-brand img,
html[data-lp-theme="light"] body.lp-auth-body .lp-side-brand img {
  filter: invert(1) hue-rotate(180deg);
}

/* the admin-mode shield lights up while the mode is on */
.lp-admin-toggle.is-on {
  color: var(--signal);
  border-color: rgba(var(--signal-rgb), 0.6);
  background: rgba(var(--signal-rgb), 0.1);
}
.lp-admin-toggle svg {
  width: 17px;
  height: 17px;
}

/* ------------------------------------------------ topbar theme flip */
[data-theme-toggle] .lp-ico-sun {
  display: none;
}
html[data-lp-theme="light"] [data-theme-toggle] .lp-ico-sun {
  display: block;
}
html[data-lp-theme="light"] [data-theme-toggle] .lp-ico-moon {
  display: none;
}

/* ------------------------------------------------ settings (Connext rows)
   A slim section nav on the left (grouped like the reference's sidebar),
   then rows: label + description left, one control right. */
.set-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 30px 56px;
}
@media (min-width: 980px) {
  .set-layout {
    grid-template-columns: 210px minmax(0, 1fr);
  }
}
.set-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-self: start;
}
@media (min-width: 980px) {
  .set-nav {
    position: sticky;
    top: 96px;
  }
}
.set-nav-group {
  margin: 20px 0 8px;
  padding-left: 12px;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--txt-dim);
}
.set-nav-group:first-child {
  margin-top: 0;
}
.set-nav button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  border: 0;
  border-left: 2px solid transparent;
  background: none;
  color: var(--txt-soft);
  font-family: var(--body);
  font-size: 14px;
  cursor: pointer;
  transition:
    background 0.2s ease,
    color 0.2s ease;
}
.set-nav button:hover {
  background: var(--surface-2);
  color: var(--txt);
}
.set-nav button.is-active {
  background: rgba(var(--signal-rgb), 0.1);
  border-left-color: var(--signal);
  color: var(--txt);
}
@media (max-width: 979px) {
  /* Narrow screens: the nav lies down into a row and wraps.

     It used to scroll instead, and a scrolling strip is the wrong shape
     for this one. It holds four fixed items, not a list that grows, and
     the group headings that would say more are hidden at this width, so
     a reader who does not think to swipe simply never learns that
     Security and Privacy exist. Four items also very nearly fit: at 14px
     they come to about 364px against the 357px a 393px phone leaves
     after .lp-content's padding, which is close enough that the strip
     looks complete while holding something back. Wrapping shows all four
     at any width and costs one extra line at most. */
  .set-nav {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
  }
  .set-nav-group {
    display: none;
  }
  .set-nav button {
    width: auto;
    white-space: nowrap;
    border-left: 0;
  }
  .set-nav button.is-active {
    background: rgba(var(--signal-rgb), 0.14);
  }
}

/* cover card: big rounded avatar tile + identity, like the reference */
.set-cover {
  display: flex;
  align-items: center;
  gap: 22px;
  padding: 22px 24px;
  margin-bottom: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
}
.set-cover-avatar {
  width: 88px;
  height: 88px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 26px;
  letter-spacing: 0.04em;
  color: #fff;
  background: var(--surface-2);
}
.set-cover-meta {
  min-width: 0;
}
.set-cover-meta h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.25rem;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
.set-cover-meta p {
  margin-top: 4px;
  color: var(--txt-dim);
  font-size: 13.5px;
  overflow-wrap: anywhere;
}

.set-section {
  max-width: 860px;
}
.set-head {
  margin: 34px 0 4px;
}
.set-head:first-child {
  margin-top: 0;
}
.set-head h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.25rem;
  letter-spacing: -0.01em;
}
.set-head p {
  margin-top: 4px;
  color: var(--txt-dim);
  font-size: 13.5px;
}
.set-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px 56px;
  padding: 24px 0;
  border-top: 1px solid var(--line);
}
.set-row:first-of-type {
  border-top: 0;
}
@media (min-width: 880px) {
  .set-row {
    grid-template-columns: 300px minmax(0, 1fr);
  }
}
.set-label h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.005em;
}
.set-label p {
  margin-top: 6px;
  color: var(--txt-dim);
  font-size: 13px;
  line-height: 1.5;
  max-width: 36ch;
}
.set-control {
  min-width: 0;
}
.set-control .field {
  margin-bottom: 0;
}
.set-control .field + .field {
  margin-top: 14px;
}
/* set-control inputs sit outside .field wrappers - same recipe */
.temple .set-control input[type="text"],
.temple .set-control input[type="url"],
.temple .set-control input[type="password"],
.temple .set-control textarea {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.5;
  padding: 13px 15px;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}
.temple .set-control textarea {
  resize: vertical;
  min-height: 120px;
}
.temple .set-control input:focus,
.temple .set-control textarea:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px rgba(var(--signal-rgb), 0.16);
}
.temple .set-control input::placeholder,
.temple .set-control textarea::placeholder {
  color: var(--txt-dim);
}
.set-actions {
  display: flex;
  justify-content: flex-end;
  gap: 14px;
  padding: 20px 0 0;
  border-top: 1px solid var(--line);
}
.char-count {
  margin-top: 6px;
  font-size: 12px;
  color: var(--txt-dim);
  text-align: right;
}
.char-count.is-max {
  color: var(--danger);
}

/* input with a trailing validity badge (the green check) */
.input-wrap {
  position: relative;
}
.input-wrap input {
  padding-right: 46px;
}
.input-badge {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: none;
  place-items: center;
  background: #22a05c;
  color: #fff;
  font-size: 12px;
  line-height: 1;
  pointer-events: none;
}
.input-wrap.is-valid .input-badge {
  display: grid;
}

/* prefixed inputs: github.com/ …  */
.prefix-input {
  display: flex;
  align-items: stretch;
}
.prefix-input .prefix {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px 0 14px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-right: 0;
  color: var(--txt-dim);
  font-family: var(--mono);
  font-size: 12px;
  white-space: nowrap;
}
.prefix-input .input-wrap {
  flex: 1;
  min-width: 0;
}
body.lp-app-body .prefix-input input {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.prefix-input + .prefix-input {
  margin-top: 12px;
}
.prefix-input .prefix svg {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  opacity: 0.8;
}

/* skill pills */
.pill-box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
}
.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--txt-soft);
  font-size: 13px;
  line-height: 1.2;
}
.pill button {
  border: 0;
  background: none;
  padding: 0 2px;
  color: var(--txt-dim);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
}
.pill button:hover {
  color: var(--danger);
}
.pill-box input {
  flex: 1 1 150px;
  min-width: 130px;
  border: 0;
  background: transparent;
  padding: 6px 4px;
  color: var(--txt);
  font-family: var(--body);
  font-size: 14px;
}
.pill-box input:focus {
  outline: none;
}
/* the inline skill input must stay bare even under the set-control recipe */
.temple .set-control .pill-box input {
  width: auto;
  flex: 1 1 150px;
  border: 0;
  background: transparent;
  padding: 6px 4px;
  font-size: 14px;
  box-shadow: none;
}
.pill-box:focus-within {
  border-color: var(--signal);
  box-shadow: 0 0 0 3px rgba(var(--signal-rgb), 0.16);
}

/* avatar hue picker */
.hue-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}
.hue-preview {
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  margin-right: 8px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt-soft);
  font-family: var(--mono);
  font-size: 17px;
  letter-spacing: 0.04em;
}
.hue-swatch {
  width: 30px;
  height: 30px;
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
  transition: transform 0.15s var(--ease);
}
.hue-swatch:hover {
  transform: scale(1.12);
}
.hue-swatch.is-active {
  border-color: var(--txt);
}
@media (prefers-reduced-motion: reduce) {
  .hue-swatch:hover {
    transform: none;
  }
}

/* toggle switch (leaderboard visibility) */
.switch-row {
  display: flex;
  align-items: center;
  gap: 14px;
}
.switch {
  position: relative;
  width: 46px;
  height: 26px;
  flex: 0 0 auto;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  cursor: pointer;
  padding: 0;
  transition:
    background 0.2s ease,
    border-color 0.2s ease;
}
.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--txt-dim);
  transition:
    transform 0.2s var(--ease),
    background 0.2s ease;
}
.switch[aria-checked="true"] {
  background: var(--signal);
  border-color: var(--signal);
}
.switch[aria-checked="true"]::after {
  transform: translateX(20px);
  background: #fff;
}
.switch-state {
  font-size: 13.5px;
  color: var(--txt-soft);
}

/* segmented pickers (language, theme) */
.seg {
  display: inline-flex;
  border: 1px solid var(--line);
}
.seg button {
  padding: 10px 18px;
  border: 0;
  background: none;
  color: var(--txt-dim);
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transition:
    background 0.2s ease,
    color 0.2s ease;
}
.seg button.is-active {
  background: var(--signal);
  color: #fff;
}
.seg-sm button {
  padding: 7px 12px;
  font-size: 10px;
}

/* read-only value row (email) */
.set-static {
  padding: 13px 15px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt-soft);
  font-size: 15px;
  overflow-wrap: anywhere;
}

/* ------------------------------------------------ press response
   Every control answers on pointerdown, not on release: small controls
   scale down a touch, large surfaces barely (0.99 reads as contact, not
   shrinkage), text buttons and list items answer in color. The way in is
   fast (~100ms); the way back out rides each element's own transition. */
.kind-toggle button:active,
.admin-tabs button:active,
.lp-bell:active {
  transform: scale(0.95);
  transition: transform 0.1s ease-out;
}
.challenge-card:active,
a.sub-row:active,
button.sub-row:active {
  transform: scale(0.99);
}
.btn-quiet:active,
.lp-topbar-back:active,
.lp-side-foot a:active {
  color: var(--txt);
}
.lp-nav-pills a:active {
  color: var(--txt);
  background: var(--surface-2);
}
.auth-card .consent:active .consent-box {
  border-color: var(--signal-soft);
}
@media (prefers-reduced-motion: reduce) {
  .kind-toggle button:active,
  .admin-tabs button:active,
  .lp-bell:active,
  .challenge-card:active,
  a.sub-row:active,
  button.sub-row:active {
    transform: none;
  }
}

/* ================================================== member stats page
   Two charts and a KPI row. Chart colours are their OWN tokens, not the
   chip text colours: a chip colour is tuned to be READ as small text, a
   chart mark is a filled shape, and the two want different steps. Both
   sets were run through the palette validator against their surface -
   dark on #161618, light on #ffffff - and clear CVD separation, the
   normal-vision floor and 3:1 contrast. Do not hand-tweak them without
   re-running that check; the pairs that matter are tight.

   The "submitted" slot is deliberately achromatic: it is the absence of
   an outcome, not an outcome, and it must not read as a fifth verdict. */
.lp-app.temple {
  --viz-accent: var(--signal);
  --viz-grid: var(--line);
  --viz-accepted: #57c08d;
  --viz-review: #f08a1e;
  --viz-submitted: #8f8f88;
  --viz-rejected: #eb5f52;
}
html[data-lp-theme="light"] body.lp-app-body .lp-app.temple {
  --viz-accepted: #1f7a45;
  --viz-review: #c2410c;
  --viz-submitted: #74747c;
  --viz-rejected: #c0392b;
}

/* ---- identity header ---- */
.mem-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 20px;
  margin-bottom: 28px;
}
.mem-avatar {
  flex: none;
  width: 72px;
  height: 72px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-family: var(--mono);
  font-size: 22px;
  letter-spacing: 0.04em;
}
.mem-id {
  flex: 1 1 260px;
  min-width: 0;
}
.mem-id h1 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.5rem, 4vw, 2rem);
  letter-spacing: -0.01em;
  line-height: 1.1;
  overflow-wrap: anywhere;
}
.mem-sub {
  margin-top: 6px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--txt-dim);
}
.mem-bio {
  margin-top: 12px;
  max-width: 52ch;
  color: var(--txt-soft);
  line-height: 1.6;
  overflow-wrap: anywhere;
}
.mem-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}

/* ---- KPI row: the numbers, before any chart ---- */
.mem-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(120px, 100%), 1fr));
  gap: 1px;
  margin-bottom: 24px;
  background: var(--line);
  border: 1px solid var(--line);
}
.mem-kpi {
  padding: 16px 18px;
  background: var(--surface);
}
.mem-kpi .k-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--txt-dim);
}
.mem-kpi .k-value {
  margin-top: 6px;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.7rem;
  line-height: 1;
  letter-spacing: -0.01em;
}
/* the one number the page leads with */
.mem-kpi.is-hero .k-value {
  font-size: 2.6rem;
  color: var(--viz-accent);
}
.mem-kpi .k-note {
  margin-top: 5px;
  font-size: 12px;
  color: var(--txt-dim);
}

/* ---- charts ---- */
.mem-charts {
  display: grid;
  /* min() inside the minmax, not a bare 300px: auto-fit collapses to one
     column on a phone but the TRACK keeps whatever floor it was given, so
     a bare 300px floor scrolls the page sideways under about 320px (a fold
     cover screen, or a desktop window at 400% zoom). Same reasoning on
     .mem-kpis and .mem-info. */
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 18px;
  margin-bottom: 24px;
}
.viz {
  padding: 18px;
  border: 1px solid var(--line);
  background: var(--surface);
  min-width: 0;
}
.viz h2 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.05rem;
}
.viz .viz-sub {
  margin-top: 3px;
  margin-bottom: 14px;
  font-size: 12.5px;
  color: var(--txt-dim);
}
.viz svg {
  display: block;
  max-width: 100%;
  overflow: visible;
}
.viz .viz-empty {
  padding: 22px 0;
  font-size: 13px;
  color: var(--txt-dim);
}
/* axis and value text wears text tokens - never the mark's colour */
.viz .v-tick {
  font-family: var(--mono);
  font-size: 10px;
  fill: var(--txt-dim);
}
.viz .v-cat {
  font-size: 12px;
  fill: var(--txt-soft);
}
.viz .v-val {
  font-family: var(--mono);
  font-size: 11px;
  fill: var(--txt);
  font-variant-numeric: tabular-nums;
}
.viz .v-grid {
  stroke: var(--viz-grid);
  stroke-width: 1;
}
.viz .v-line {
  fill: none;
  stroke: var(--viz-accent);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.viz .v-area {
  fill: var(--viz-accent);
  opacity: 0.1;
  stroke: none;
}
/* the 2px surface ring keeps a dot legible where it crosses the line */
.viz .v-dot {
  fill: var(--viz-accent);
  stroke: var(--surface);
  stroke-width: 2;
}
.viz .v-bar-track {
  fill: var(--viz-grid);
  opacity: 0.5;
}
.viz .v-hit {
  fill: transparent;
  cursor: default;
}
.viz-tip {
  position: absolute;
  z-index: 5;
  padding: 7px 10px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--txt);
  font-size: 12px;
  line-height: 1.45;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.viz-tip.is-on {
  opacity: 1;
}
.viz-tip .tip-k {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--txt-dim);
}
.viz-wrap {
  position: relative;
}

/* legend: identity never rides on colour alone */
.viz-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin-top: 14px;
}
.viz-legend span {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--txt-soft);
}
.viz-legend i {
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 2px;
}

/* ---- history table ---- */
/* the history is the table view for both charts, so it gets the page's
   width rather than the platform's narrower reading-panel cap */
.mem-history {
  max-width: none;
}
.mem-history .h-row {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-top: 1px solid var(--line);
}
.mem-history .h-row:first-of-type {
  border-top: 0;
}
.mem-history .h-title {
  min-width: 0;
  overflow-wrap: anywhere;
}
.mem-history .h-score {
  font-family: var(--mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--txt);
}
.mem-history .h-date {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--txt-dim);
}

/* board rows became links to these pages */
a.board-row {
  text-decoration: none;
  color: inherit;
}
a.board-row:hover {
  border-color: rgba(var(--signal-rgb), 0.45);
}
a.board-row:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
}

@media (max-width: 640px) {
  .mem-kpi .k-value {
    font-size: 1.45rem;
  }
  /* five tiles in a two-column grid leave a hole; the hero takes the
     full first row and the other four pair up under it */
  .mem-kpi.is-hero {
    grid-column: 1 / -1;
  }
  .mem-kpi.is-hero .k-value {
    font-size: 2.1rem;
  }
  /* title + chip on top, score and date sharing the line beneath */
  .mem-history .h-row {
    grid-template-columns: 1fr auto;
    row-gap: 6px;
  }
  .mem-history .h-score {
    grid-row: 2;
    grid-column: 1;
  }
  .mem-history .h-date {
    grid-row: 2;
    grid-column: 2;
    text-align: right;
  }
}

/* ------------------------------------------- challenge editor: Finnish
   translation drawer. Closed by default on untranslated challenges so
   the editor stays one screen; opens itself when a translation exists. */
.editor-fi {
  margin: 18px 0;
  border: 1px solid var(--line);
  background: var(--surface-2);
  padding: 0 16px;
}
.editor-fi summary {
  cursor: pointer;
  padding: 12px 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--txt-soft);
  -webkit-user-select: none;
  user-select: none;
}
.editor-fi summary .field-hint {
  text-transform: none;
  letter-spacing: normal;
  font-family: inherit;
}
.editor-fi summary::marker {
  color: var(--signal);
}
.editor-fi[open] {
  padding-bottom: 14px;
}

/* prize mention on cards and in the challenge meta - the one meta item
   allowed to wear the accent, since it is the one people scan for */
.card-prize {
  color: var(--signal-soft);
}

/* =====================================================================
   MOBILE ERGONOMICS
   Two problems, kept together because they are both about surviving a
   phone: text that cannot be assumed to break, and controls that were
   sized for a mouse.
   ===================================================================== */

/* ---- 1. remote text must never widen the page -----------------------
   Every string on a temple page arrives from the database: display
   names (40 chars, no space required), challenge titles an admin types
   (120), pasted https:// links, free-text briefs and feedback. None of
   them is guaranteed to carry a break opportunity, and one that does
   not pushes its card - and with it the whole document - wider than the
   viewport, which is how a phone ends up scrolling sideways.

   `anywhere` rather than `break-word` is deliberate: only `anywhere`
   also shrinks the min-content width of the flex and grid items holding
   the text, and it is that min-content floor - not the painted glyphs -
   that was forcing the cards wide. Scoped to `.temple` (the app
   shell, the landing main, the auth card) so the shared site chrome
   outside it keeps the site's own wrapping. */
.temple {
  overflow-wrap: anywhere;
}
/* the mono meta lines are the exception: "closes in 9d 07h 12m 30s" and
   the status chips read as single units, so they wrap between their
   items (the rows already flex-wrap) instead of mid-token */
.temple .card-meta > *,
.temple .challenge-meta-row > *,
.temple .ab-meta > *,
.temple .sub-meta > * {
  overflow-wrap: normal;
}

/* the review queue's challenge filter takes its intrinsic width from the
   longest title in the list, so a wordy challenge made this one select
   wider than the phone. Cap it against the filter row and let the text
   trail off. */
.temple .admin-filters select {
  min-width: 0;
  max-width: 100%;
  text-overflow: ellipsis;
}

/* ---- selects need appearance:none to have a height at all -----------
   WebKit refuses to apply padding, height OR min-height to a select
   still wearing its native appearance, so every select here rendered
   about 20px tall on iOS (and inconsistently on desktop Safari) while
   the inputs beside them sat at their designed height. Dropping the
   native appearance is the only way to give them one, which means
   drawing the arrow ourselves.

   The arrow is two gradients rather than a background SVG so it takes
   its colour from `currentColor` and therefore follows the light/dark
   theme with no second asset - and it needs nothing from img-src. */
.temple select,
.lp-auth-body select {
  -webkit-appearance: none;
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position:
    calc(100% - 17px) calc(50% - 1px),
    calc(100% - 12px) calc(50% - 1px);
  background-size:
    5px 5px,
    5px 5px;
  background-repeat: no-repeat;
  /* room for the arrow, whatever horizontal padding the variant sets */
  padding-right: 34px;
}
/* Safari also ignores padding on the native control, so these variants
   had never actually shown their designed height there either */
.temple .admin-filters select {
  padding-block: 9px;
}
.temple .field select,
.lp-auth-body .field select {
  padding-block: 13px;
  /* the padding shorthand above set 15px, which would sit the text under
     the arrow this rule's counterpart draws */
  padding-right: 34px;
}
.temple .dtp-time select {
  padding-block: 8px;
  padding-right: 28px;
  background-position:
    calc(100% - 14px) calc(50% - 1px),
    calc(100% - 9px) calc(50% - 1px);
}

/* ---- 2. touch targets ----------------------------------------------
   Everything below only fires under the same 767px breakpoint the site's
   mobile.css uses, and only grows hit areas - no visual redesign. Where
   a control's size is part of the design (the avatar, the icon buttons,
   the logo) the box stays put and an absolutely-positioned ::before
   carries the extra reach, the idiom mobile.css already uses for the
   site's hamburger. The pseudo-element is positioned, so it never
   becomes a grid item in the `place-items: center` controls.

   Two arms, because "needs a finger" and "is narrow" are different
   questions. The width arm covers a phone and a desktop window cropped
   down to phone width. The pointer arm is the one that matters for a
   tablet: an iPad reports 768-1366px and is driven entirely by touch, so
   a width-only query left every control on it back at mouse size. The
   `(hover: none) and (pointer: coarse)` pairing is the same test
   mobile.css already uses to spot a touch device. */
@media (max-width: 767px), (hover: none) and (pointer: coarse) {
  /* icon buttons (theme, admin shield) 36px and the avatar 32px: both
     keep their drawn size, both answer to 44px+ of finger */
  .temple .lp-bell,
  .temple .lp-avatar {
    position: relative;
  }
  .temple .lp-bell::before {
    content: "";
    position: absolute;
    inset: -5px; /* 36 -> 46 */
  }
  .temple .lp-avatar::before {
    content: "";
    position: absolute;
    inset: -7px; /* 32 -> 46 */
    border-radius: 50%;
  }
  /* The brand gets height instead of a ::before ring, for two reasons: its
     drawn height is not a constant (21px where the Temple badge sets it,
     26px where the logo does), so a fixed inset lands short on some
     viewports; and where the menu strip wraps to its own row, a downward
     ring reaches into it and competes for the same taps. Growing the flex
     item cannot leave its own line, and costs no header height here - the
     topnav row is already past 100px wherever this query applies. */
  .temple .lp-side-brand {
    min-height: 44px;
  }

  /* the menu strip: 42px was one pixel-pair short */
  .temple .lp-nav-pills a {
    padding-top: 11px;
    padding-bottom: 11px;
  }

  /* tab rows: settings' section strip, the admin console's tabs, the
     login card's sign-in / create-account pair, the EN/FI switch */
  .temple .set-nav button,
  .temple .admin-tabs button,
  .temple .seg button {
    min-height: 44px;
  }
  .temple .seg button,
  .temple .admin-tabs button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  /* the segmented switches are icon-narrow (EN / FI); widen rather than
     leaving a 38px-wide target */
  .temple .seg-sm button {
    min-width: 52px;
  }

  /* the avatar hue picker: nine 30px squares in a wrapping row. The row
     already wraps, so the squares can simply become finger-sized. */
  .temple .hue-swatch {
    width: 44px;
    height: 44px;
  }

  /* skill pills: the remove "x" was a 14px glyph with 2px of padding -
     the smallest target in the platform. It becomes a full-height
     end-cap of the pill instead, which needs no expansion beyond the
     pill's own bounds and so cannot overlap its neighbour. */
  .temple .pill {
    min-height: 44px;
    padding: 0 0 0 14px;
    gap: 0;
  }
  .temple .pill button {
    align-self: stretch;
    width: 44px;
    min-height: 44px;
    padding: 0;
    display: grid;
    place-items: center;
    font-size: 15px;
  }
  /* the "add a skill" input shares the box with the pills */
  .temple .pill-box input,
  .temple .set-control .pill-box input {
    min-height: 44px;
  }

  /* link chips (a member's website and socials) are anchors off to other
     sites, so a mis-tap costs a page load; the status chips beside them
     are not interactive and keep their small size */
  .temple a.chip {
    min-height: 44px;
    padding-inline: 14px;
  }

  /* filter row: selects and the name search sat at 35-37px */
  .temple .admin-filters select,
  .temple .admin-filters input[type="search"] {
    min-height: 44px;
  }
  /* every other select in the platform (add-entry pickers, the challenge
     editor's status and mode, the scheduler's hour and minute) */
  .temple select {
    min-height: 44px;
  }

  /* the shared .btn recipe lands a pixel or two under the line on a
     phone; hold the platform's buttons at the full target and centre the
     label now that the box can be taller than its text */
  .temple .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
  }

  /* underlined text actions ("Forgot password?", "Cancel") */
  .temple .btn-quiet,
  .temple .lp-topbar-back {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* the landing's "The platform →" sits on a shared baseline with its
     section heading, so it grows by pseudo-element rather than by
     min-height, which would move that baseline */
  .temple .lp-section-head a {
    position: relative;
  }
  .temple .lp-section-head a::before {
    content: "";
    position: absolute;
    inset: -12px; /* 20 -> 44 tall */
  }
}

/* ---- 3. the menu strip scrolls: say so ------------------------------
   Under 720px the pills drop to their own row and scroll horizontally, and
   on a tablet they stay inline but the admin set (seven items) still
   outruns the space left beside the brand. Either way some items sit
   off-screen with nothing to suggest they exist, so the strip fades on
   whichever side still has track.

   No width query here on purpose: lp.js measures the actual scroll
   geometry and sets `fade-left` / `fade-right` only when there is really
   something that way, which is the same answer at every viewport. With
   neither class the mask is a fully opaque no-op, so a desktop strip that
   fits shows nothing - and that is also the pre-JS default. */
.lp-nav-pills {
  --pills-fade-start: 0px;
  --pills-fade-end: 0px;
  /* a mask rather than a gradient overlay so it works on any theme
     ground, light or dark, with no second element to position */
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 var(--pills-fade-start),
    #000 calc(100% - var(--pills-fade-end)),
    transparent 100%
  );
  mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 var(--pills-fade-start),
    #000 calc(100% - var(--pills-fade-end)),
    transparent 100%
  );
}
.lp-nav-pills.fade-left {
  --pills-fade-start: 26px;
}
.lp-nav-pills.fade-right {
  --pills-fade-end: 26px;
}

/* ---- 4. the menu becomes a bottom bar ------------------------------
   On a phone, on a tablet and in a cropped window the menu leaves the
   header and docks to the bottom of the screen, the way a native app
   puts its sections under the thumb. The header keeps what identifies
   you rather than what navigates: the brand on the left, the theme
   switch, avatar and admin toggle on the right.

   Two questions again, the pair the rest of this file uses. Width
   catches a cropped desktop window, and `(hover: none) and (pointer:
   coarse)` catches a touch device at any size, which is what puts an
   iPad Pro in landscape on the bar as well - it is a finger-driven
   screen whatever its width says. 1024px is where a mouse-driven window
   still has room to carry the strip inline beside the brand.

   No markup moves for this: it is the same <nav> the header held, so
   the current-page marking, the scroll-into-view and the edge fades in
   lp.js all keep working with nothing to teach them. */
@media (max-width: 1023.98px), (hover: none) and (pointer: coarse) {
  /* one number for the bar's height and the space that clears it, so
     the two cannot drift apart */
  .lp-app {
    --lp-tabbar: 62px;
  }

  .lp-nav-pills {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    height: calc(var(--lp-tabbar) + env(safe-area-inset-bottom, 0px));
    /* the home indicator on a modern iPhone sits over the last ~34px of
       the screen; the bar grows by exactly that so its targets stay
       above it rather than under a system gesture area */
    padding-bottom: env(safe-area-inset-bottom, 0px);
    gap: 0;
    /* stretch, not center: an item that only wraps its own text is about
       39px tall, and the whole point of the bar is that the strip is a
       row of full-height targets */
    align-items: stretch;
    background: var(--bg);
    border-top: 1px solid var(--line);
  }

  /* icon over label. Each item claims an equal share of the bar while
     the labels fit, and the strip scrolls rather than truncating a word
     when they stop: the six-item admin menu crosses that line at about
     360px, and the fade already says which way the rest is. */
  .lp-nav-pills a {
    flex: 1 0 auto;
    min-width: 64px;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    padding: 0 8px;
    font-size: 10px;
    line-height: 1.2;
    letter-spacing: 0.02em;
    text-align: center;
  }
  .lp-nav-pills .lp-ico {
    width: 22px;
    height: 22px;
  }

  /* The inverted pill is a header idiom. Down here a solid block behind
     one item reads as a button in a row of labels rather than as the
     page you are on, so the current tab is named the way a tab bar names
     it: full-strength colour, heavier label, opaque icon. */
  .lp-nav-pills a[aria-current="page"] {
    background: none;
    color: var(--txt);
    font-weight: 600;
  }
  .lp-nav-pills a[aria-current="page"] .lp-ico {
    opacity: 1;
  }

  /* the bar floats over the page, so the page has to end above it */
  .lp-content {
    padding-bottom: calc(
      var(--lp-tabbar) + env(safe-area-inset-bottom, 0px) + 24px
    );
  }
}
