/* Site-specific overrides on top of Pico.css — most of what nav/table
   styling needed is handled by Pico itself now, so this stays close to
   empty until a real one-off tweak is needed. */

/* Global spacing rhythm: form elements were carrying a lot of unused
   vertical padding by default; horizontal padding was already fine and is
   left alone. Typography/block vertical spacing (margins) trimmed a
   little too. */
:root {
  --pico-form-element-spacing-vertical: 0.35rem;
  --pico-typography-spacing-vertical: 0.85rem;
  --pico-block-spacing-vertical: 0.85rem;
}

/* Pico's root font-size scales up at wider viewports (100% -> 131.25% at
   1536px+); since the whole theme is rem-based, that alone was making
   headings/buttons/spacing feel oversized at normal desktop widths — the
   equivalent of viewing the site at 125-131% zoom. Overriding the same
   breakpoints at 0.8x each (round numbers by design: 100/106.25/112.5/
   118.75/125/131.25 x 0.8 = 80/85/90/95/100/105) reproduces the ~80%
   browser-zoom look the user asked for, at every screen size, without
   losing Pico's fluid scaling between breakpoints. Same specificity as
   Pico's own :host,:root rule at each breakpoint — wins because style.css
   loads after pico.min.css, same as the accent-color override above. */
:root {
  --pico-font-size: 80%;
}

@media (min-width: 576px) {
  :root {
    --pico-font-size: 85%;
  }
}

@media (min-width: 768px) {
  :root {
    --pico-font-size: 90%;
  }
}

@media (min-width: 1024px) {
  :root {
    --pico-font-size: 95%;
  }
}

@media (min-width: 1280px) {
  :root {
    --pico-font-size: 100%;
  }
}

@media (min-width: 1536px) {
  :root {
    --pico-font-size: 105%;
  }
}

/* Pico colors every plain link purple by default (base state, not just
   hover) — with as many links as this site has (nav, every table row
   title, "read more" links...), that made purple the dominant text color
   site-wide, leaving it no room to actually mean anything as a highlight.
   Demoting the resting state to normal body text + underline (so links
   are still identifiable without relying on color) and keeping Pico's
   own hover/focus/active/[aria-current] state untouched restores purple
   to hover/current-page/buttons/favourite-tracks only — the places it's
   meant to draw the eye. `color: inherit` (a real property, not a custom
   one) deliberately sidesteps Pico's --pico-color custom-property chain,
   which the link rule redefines locally to the accent purple — inherit
   just pulls whatever the surrounding text color already is. Specificity
   here (0,1,1) beats Pico's own :where()-wrapped base rule (0 by design)
   without needing !important or source-order games.

   User liked this overall (less purple, tables/headers reading as normal
   text) but flagged it as a "maybe revert the color part later" — kept
   as a live open question, not settled. If reverting: delete just this
   rule (the hover/focus/active/[aria-current] rule below is independent
   and was NOT flagged for reversion, leave it). Pico's own default
   (:where(a:not([role=button])){--pico-color:var(--pico-primary)}) takes
   over automatically once this is gone — no other change needed. */
a:not([role=button]) {
  color: inherit;
  text-decoration-color: currentColor;
}

/* Split deliberately: Pico's own version of this rule groups [aria-current]
   in with :hover/:focus/:active, all sharing --pico-primary-hover — a
   paler, lightened-for-hover shade, not the base accent. That meant the
   sidebar's current-page link (the single most visible piece of "purple
   text" on any given page) was never actually showing --pico-primary at
   all, no matter how that variable got tuned — this is what was really
   behind "the purple still looks pale/washed out" even after bumping
   --pico-primary itself twice. Current-page now uses the base color;
   true :hover/:focus/:active (transient, mouse-over-the-link-right-now)
   keeps the lighter hover shade, which is a reasonable extra affordance
   on top of a merely-current link. */
a:not([role=button]):is(:hover, :active, :focus) {
  color: var(--pico-primary-hover);
  text-decoration-color: var(--pico-primary-hover-underline);
}

a:not([role=button])[aria-current]:not([aria-current=false]) {
  color: var(--pico-primary);
  text-decoration-color: var(--pico-primary-underline);
}

/* Opt-in purple for the handful of links that are the point of a bit of
   UI (a view-toggle like History's "Best of ROTW only"/"All history"),
   not incidental body content — everything else in a:not([role=button])
   above is deliberately grey-by-default, but a toggle like this one is
   closer in spirit to a button/current-page link than to a random inline
   link, so it keeps the accent color at rest instead of only on hover. */
.link-accent {
  color: var(--pico-primary);
  text-decoration-color: var(--pico-primary-underline);
}

.link-accent:is(:hover, :focus, :active) {
  color: var(--pico-primary-hover);
  text-decoration-color: var(--pico-primary-hover-underline);
}

/* A bare checkmark next to a form field doesn't say what it confirms —
   spelling it out (italic, accent-colored so it reads as metadata rather
   than body text) on the Artist page's country/tags confirmation state. */
.confirmed-label {
  color: var(--pico-primary);
  font-style: italic;
}

/* Accent color: purple, not Pico's default blue — anchored on the same
   purple already used for favourite tracks and this community's Discord
   staff role color (#9649e7), not picked arbitrarily. Same selectors Pico
   itself uses for its two themes, so source order (this file loads after
   pico.min.css) decides the override cleanly. Light gets a deepened shade
   for text contrast on a white background; dark stays close to the
   Discord reference, which already reads well on a dark background. */
:root:not([data-theme=dark]),
[data-theme=light] {
  --pico-primary: #6d28d9;
  --pico-primary-background: #6d28d9;
  --pico-primary-border: #6d28d9;
  --pico-primary-hover: #5b21b6;
  --pico-primary-hover-background: #5b21b6;
  --pico-primary-hover-border: #5b21b6;
  --pico-primary-focus: rgba(109, 40, 217, 0.375);
  --pico-primary-underline: rgba(109, 40, 217, 0.5);
  --pico-primary-hover-underline: #5b21b6;
  --pico-primary-inverse: #fff;
}

/* --pico-primary (text/underline — links now only show this on
   hover/focus/current, see the link-color rules above; also favourite
   tracks) was a paler, more desaturated violet than
   --pico-primary-background (buttons) — the user wanted purple text to
   use that same button punch rather than a separate, washed-out shade.
   Went through two revisions before landing here: #b794f6 (original) →
   #a78bfa (violet-400) was reported still too pale, but that turned out
   to be a real bug, not this color being wrong — the rule below this one
   was grouping [aria-current] in with :hover/:focus/:active, so the
   sidebar's current-page link (the most visible purple text on any given
   page) was showing --pico-primary-hover (#c9b3ff, a *lighter* hover
   shade) no matter what --pico-primary itself was set to. Bumped to
   violet-500 #8b5cf6 to compensate for a problem that wasn't actually
   about this variable; once the real bug got fixed (current-page now
   uses --pico-primary directly, see above), reverted back to violet-400
   #a78bfa here — better contrast (~6.6:1 vs. ~4.3:1, clears WCAG AA for
   normal text) and should now actually be visible where it matters. */
@media only screen and (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --pico-primary: #a78bfa;
    --pico-primary-background: #7c3aed;
    --pico-primary-border: #7c3aed;
    --pico-primary-hover: #c9b3ff;
    --pico-primary-hover-background: #8b5cf6;
    --pico-primary-hover-border: #8b5cf6;
    --pico-primary-focus: rgba(167, 139, 250, 0.375);
    --pico-primary-underline: rgba(167, 139, 250, 0.5);
    --pico-primary-hover-underline: #c9b3ff;
    --pico-primary-inverse: #fff;
  }
}

/* Same dark values, applied when the theme toggle below sets data-theme
   explicitly (overriding the OS preference) rather than relying on it. */
[data-theme=dark] {
  --pico-primary: #a78bfa;
  --pico-primary-background: #7c3aed;
  --pico-primary-border: #7c3aed;
  --pico-primary-hover: #c9b3ff;
  --pico-primary-hover-background: #8b5cf6;
  --pico-primary-hover-border: #8b5cf6;
  --pico-primary-focus: rgba(167, 139, 250, 0.375);
  --pico-primary-underline: rgba(167, 139, 250, 0.5);
  --pico-primary-hover-underline: #c9b3ff;
  --pico-primary-inverse: #fff;
}

/* Sun/moon-flanked toggle switch (replaces a single emoji-swapping
   button) — the checkbox itself is invisible (opacity:0) but still real
   and focusable, stacked exactly over .theme-switch-track so keyboard/
   screen-reader interaction and the :checked selector below both keep
   working; only the track+thumb are drawn for sighted mouse/touch use. */
.theme-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  cursor: pointer;
  line-height: 1;
}

.theme-switch-icon {
  font-size: 0.9rem;
}

.theme-switch-track {
  position: relative;
  display: inline-block;
  width: 2.1em;
  height: 1.15em;
  border-radius: 999px;
  /* --pico-muted-border-color alone (a subtle divider gray) reads as
     near-flat against the dropdown panel's own similarly neutral
     background in both themes — mixing in some --pico-primary gives the
     track real, theme-independent contrast against its surroundings
     without needing separate light/dark-specific values. */
  background: color-mix(in srgb, var(--pico-primary) 30%, var(--pico-muted-border-color));
}

.theme-switch-checkbox {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.theme-switch-thumb {
  position: absolute;
  top: 0.1em;
  left: 0.1em;
  width: 0.95em;
  height: 0.95em;
  border-radius: 50%;
  background: var(--pico-background-color);
  transition: transform 0.15s ease;
}

.theme-switch-checkbox:checked ~ .theme-switch-thumb {
  transform: translateX(0.95em);
  background: var(--pico-primary);
}

.theme-switch-checkbox:focus-visible ~ .theme-switch-thumb {
  outline: 2px solid var(--pico-primary);
  outline-offset: 2px;
}

/* Role-color dots, mirroring this community's existing Discord role
   colors so people recognize who's who the same way they already do in
   Discord. Seven badges, more granular than the five backend roles —
   Founder/Webmaster are both `is_staff` (distinguished only by "Founder"
   group membership) and Superguest/Recommender are both plain normal
   users (distinguished only by "Superguest" group membership); see
   role_dot_class()/role_label() in accounts/templatetags/role_tags.py for
   the precedence chain. Webmaster and Recommender intentionally share a
   color for now (may change later). */
.role-dot {
  display: inline-block;
  vertical-align: middle;
  width: 0.55em;
  height: 0.55em;
  border-radius: 50%;
  margin-right: 0.45em;
}

.role-dot-superuser {
  background-color: #84cc16;
}

.role-dot-founder {
  background-color: #9649e7;
}

.role-dot-webmaster {
  background-color: #e9be10;
}

.role-dot-frontman {
  background-color: #e61d61;
}

.role-dot-recommender {
  background-color: #e9be10;
}

.role-dot-superguest {
  background-color: #c2410c;
}

.role-dot-guest {
  background-color: #11806a;
}

.app-shell {
  display: grid;
  grid-template-columns: 288px 1fr;
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "topbar topbar"
    "sidebar content";
  min-height: 100vh;
}

.topbar {
  grid-area: topbar;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.4rem 1.5rem;
  /* Needed so the topbar (and the hamburger button inside it) still
     paints above the mobile sidebar overlay below — that sidebar is
     position:fixed with z-index:100, and an unpositioned element like
     this one would otherwise always paint underneath any positioned
     element it overlaps, regardless of z-index. Without this, opening
     the mobile nav covered the hamburger button with the sidebar itself,
     leaving no way to close it again except re-tapping blindly under the
     sidebar or navigating away. */
  position: relative;
  z-index: 101;
  /* Solid brand-purple bar instead of a thin unstyled strip with a muted
     border — the "go a bit crazy" option out of three topbar-pop
     alternatives the user picked. --pico-primary-inverse (already #fff
     in both themes, see the accent-color block above) makes the plain
     text/icons readable against it; the shadow (replacing the old
     border-bottom, which would have clashed with a solid fill) is what
     actually lifts the bar off the page instead of just recoloring it. */
  background: var(--pico-primary-background);
  color: var(--pico-primary-inverse);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}

/* Plain links/icons in the topbar (site-title, hamburger, account badge)
   would otherwise resolve to the site's demoted grey link color, or the
   hover/current-page purple — both invisible or low-contrast against a
   solid purple bar. Forcing the inverse color here, and just dimming
   (not recoloring) on hover, keeps interactive affordance without
   fighting the fill. */
.topbar a,
.topbar .hamburger {
  color: var(--pico-primary-inverse);
}

.topbar a:is(:hover, :focus, :active) {
  color: var(--pico-primary-inverse);
  opacity: 0.75;
}

/* The account dropdown's own popup menu (the "... profile" row) is NOT
   painted on the solid-purple bar itself — Pico renders it as a separate
   floating panel with its own (theme-neutral) --pico-dropdown-background-
   color/--pico-dropdown-color. `.topbar a` above would otherwise force
   that row's text white regardless, which is invisible-to-poor contrast
   against Pico's normal light/dark dropdown surface. Needs more classes
   than `.topbar a` (2 vs 1) to actually win here. */
.topbar .account-dropdown > summary + ul a {
  color: var(--pico-dropdown-color);
}

.topbar .account-dropdown > summary + ul a:is(:hover, :focus, :active) {
  color: var(--pico-dropdown-color);
  opacity: 1;
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}

.topbar .site-title {
  font-weight: bold;
  /* One step up from the surrounding topbar/body text (which is plain
     1em) — asked for alongside the solid-purple topbar so the title
     reads as the anchor of the new, more prominent bar. */
  font-size: 1.15em;
}

.nav-toggle {
  display: none;
}

.hamburger {
  display: none;
  cursor: pointer;
  font-size: 1.5rem;
  line-height: 1;
}

/* Icon lives entirely in CSS (no glyph in the HTML) so the open/closed
   state can be swapped via the existing checkbox-hack sibling selector,
   same pattern as `.nav-toggle:checked ~ .sidebar` below — no JS needed. */
.hamburger::before {
  content: "\2630";
}

.nav-toggle:checked ~ .topbar .hamburger::before {
  content: "\2715";
}

.topbar .account-links {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}

/* Pico's bare `details{margin-bottom:var(--pico-spacing)}` still applies
   here — Pico only zeroes it for `nav details.dropdown`, and this one
   isn't inside a <nav>. That stray bottom margin enlarges the dropdown's
   own margin box without .account-links' align-items:center treating it
   as centerable space, so the visible summary/username sat noticeably
   above the row's true center — same root cause already fixed once for
   the sidebar's own <details> categories (see `.sidebar nav > ul > li >
   details` below). */
.topbar .account-dropdown {
  margin-bottom: 0;
}

/* Scoped to a direct child on purpose: the account dropdown's own menu
   (the "... profile" row, built by user_badge()) is an <a> too, several
   levels deeper inside <details><ul><li> — matching it here as well
   would win over Pico's own `details.dropdown>summary+ul li a{display:
   block;...}` (this selector has more classes, so it wins on specificity
   regardless of load order) and break that row back down to inline-flex
   sizing instead of the full-width, hover-highlighted menu row Pico
   already gives it for free. */
.topbar .account-links > a {
  display: inline-flex;
  align-items: center;
}

/* display:contents removes the <form>'s own box from layout entirely, so
   its button becomes a direct child box of whatever actually contains it
   instead of carrying Pico's own default form margin on top — before the
   account dropdown existed, that "whatever" was .account-links' flex row
   directly; now it's the dropdown menu's <li>, where the same stray
   margin would otherwise show up as extra dead space under the Log out
   row. Either way, the form's own box is never wanted. */
.topbar .inline-form {
  display: contents;
}

.sidebar {
  grid-area: sidebar;
  border-right: 1px solid var(--pico-muted-border-color);
  /* A purple tint ties the sidebar to the site's accent color without
     competing with the actual purple highlights (current page, buttons)
     inside it. color-mix against --pico-background-color rather than a
     flat rgba works in both themes for free.

     Light mode's --pico-background-color is near-white, so a mix that
     reads as a clear tint against a dark background (5%) was nearly
     invisible here — user reported the sidebar looked "nearly white",
     indistinguishable from .content. Went 5% -> 12% -> 16% (final value
     after a follow-up request for more) for light. Dark mode was
     reported as "fine, though could be slightly increased" — went
     5% -> 8% -> 10%, same media-query/[data-theme] override pattern as
     the --pico-primary tuning above, so an explicit toggle and the OS
     preference both get the right value. */
  background: color-mix(in srgb, var(--pico-primary) 16%, var(--pico-background-color));
  padding: 0.5rem 1.25rem;
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  overflow-y: auto;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .sidebar {
    background: color-mix(in srgb, var(--pico-primary) 10%, var(--pico-background-color));
  }
}

[data-theme=dark] .sidebar {
  background: color-mix(in srgb, var(--pico-primary) 10%, var(--pico-background-color));
}

.sidebar nav > ul {
  list-style: none;
  padding-left: 0;
  margin-bottom: 0;
}

/* Pico's own `nav li{display:inline-block;margin:0;padding:1rem 0.5rem}`
   was still in effect here — this rule only ever overrode margin-bottom
   (higher specificity than Pico's thanks to the .sidebar ancestor class,
   so that part won), leaving inline-block layout and 1rem top+bottom
   padding untouched underneath. Inline-block items lay out as an inline
   formatting context (line-height/vertical-align governs the gap between
   wrapped "lines"), which made the gap between items of different
   intrinsic height (a tall <details> vs. a plain link) content-dependent
   and inconsistent instead of a predictable fixed margin — the actual
   cause of the "History and stats"/"Polls" gaps being both bigger *and*,
   after the first margin-only fix, sometimes smaller than the rest.
   Forcing real block layout with padding zeroed makes every top-level
   item's spacing purely a function of this one margin-bottom, full stop —
   which also means, now that it's the only variable left, it's a
   deliberate design choice rather than an accident: 1.5rem gives clear
   visual separation between top-level categories, distinctly more than
   the ~0.85em-scaled nested sub-item list below (`.sidebar details ul`,
   untouched) — a two-tier rhythm (loose between groups, tight within
   one) rather than one flat spacing everywhere. Was 0.4rem while this
   rule was still just chasing the inline-block/padding/line-height bugs
   above; once those were actually fixed, that value read as too tight
   across the board — this is the first pass where margin-bottom is being
   picked for how it should look, not just to cancel out a Pico default. */
.sidebar nav > ul > li {
  display: block;
  margin: 0 0 1.5rem;
  padding: 0;
}

/* One more Pico default on the pile above: `nav li :where(a,[role=link])`
   gives the <a> itself — not just the <li> — display:inline-block plus
   0.5rem padding offset by a matching -0.5rem margin (a click-target-
   enlarging trick that's meant to net to zero visually). <details>/
   <summary> never goes through this rule at all (it only matches
   a/[role=link]), so it stayed invisible everywhere a <details> sat on
   at least one side of a gap — masked by the same kind of coincidence as
   the old "nested spacing looks fine" case — and only showed up clearly
   between two genuine plain links (Announcements->ROTW measured ~9px
   wider than every other top-level gap, matching 0.5rem almost exactly).
   Resetting it here makes a top-level <a>'s box exactly as bare as a
   <details>'s already is, so the two are finally, actually equivalent. */
.sidebar nav > ul > li > a {
  display: block;
  margin: 0;
  padding: 0;
}

/* First-level entries only (flat links like "ROTW", and category headers
   like "History and stats") — bold, and the same weight/color whether
   they're a plain link or a <details><summary> category, which previously
   looked like two different kinds of thing (link = purple + normal
   weight, summary = plain unstyled text) purely because <summary> never
   got Pico's link styling at all. Nested links one level down (inside a
   category) intentionally stay regular weight at the smaller font-size
   below, so the bold marks "top-level category", not "any link". */
.sidebar nav > ul > li > a,
.sidebar nav > ul > li > details > summary {
  font-weight: bold;
}

/* Pico hardcodes `details summary{line-height:1rem}` — shorter than the
   1.5 line-height a plain <a> gets from the page default. Even with
   identical box margins (see the display:block fix above), a shorter
   line box shifts the summary's own text baseline closer to whatever
   comes next, so any gap touching a <details> read as tighter than a
   plain-link-to-plain-link gap — worst for "History and stats"->"Polls",
   where *both* sides were compressed. Matching line-height here removes
   the last source of per-item height variation; margin-bottom is now
   genuinely the only thing controlling spacing between top-level items. */
.sidebar nav > ul > li > details > summary {
  line-height: var(--pico-line-height);
}

/* Pico gives every <details> its own margin-bottom (details{margin-
   bottom:var(--pico-spacing)}), on top of the 0.4rem margin-bottom this
   sidebar's <li> already carries — the two don't collapse into each
   other here, they stack. That's what made the gap after "History and
   stats"/"Polls" (both <details>) visibly bigger than the gap after a
   plain link like "ROTW"/"Blog": every category was silently getting
   ~1.3x the spacing a flat link got. Zeroing it here leaves the <li>'s
   own margin as the only source of vertical rhythm, so every top-level
   item — link or category — sits the same distance from the next one. */
.sidebar nav > ul > li > details {
  margin-bottom: 0;
}

.sidebar details ul {
  list-style: none;
  padding-left: 1rem;
  font-size: 0.85em;
}

.sidebar details summary {
  cursor: pointer;
}

.content {
  grid-area: content;
  padding: 0.85rem 2rem;
  /* Without this, a wide unwrapped table forces the grid track (and with
     it the topbar, which spans the full row) to grow past the viewport.
     The .table-scroll wrapper below is what actually scrolls. */
  min-width: 0;
  overflow-x: hidden;
}

/* Wraps every {% render_table %} — gives wide tables their own horizontal
   scrollbar instead of pushing the page (and topbar) wider. */
.table-scroll {
  overflow-x: auto;
  margin-bottom: 0.85rem;
}

.table-scroll table {
  font-size: 0.9em;
}

/* django-tables2 puts "orderable" on every sortable <th>, plus "asc"/"desc"
   on whichever column is the current sort (see BoundColumn.get_th_class in
   django_tables2/columns/base.py) — these arrows key off those classes
   rather than anything this project adds itself. Unsorted-but-sortable
   columns get the two-way glyph as a discoverability hint; the active sort
   column swaps it for a one-way arrow showing current direction. */
.table-scroll th.orderable a::after {
  content: "\21C5";
  margin-left: 0.35em;
  opacity: 0.55;
}

.table-scroll th.orderable.asc a::after {
  content: "\2191";
  opacity: 1;
}

.table-scroll th.orderable.desc a::after {
  content: "\2193";
  opacity: 1;
}

/* django-tables2's default pagination markup is a bare <ul class="pagination">
   of <li>s (previous / page numbers / next) with no layout rules of its
   own — left alone, each <li>'s default list-item display stacks them one
   per line instead of the single previous-1-2-3-next row this is meant to
   read as. */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  list-style: none;
  padding-left: 0;
  margin: 0.5rem 0 0;
}

/* `list-style: none` above only clears the marker on the <ul> itself —
   Pico's own `ul li{list-style:square}` sets it directly on each <li>,
   which wins over the ancestor's value regardless of specificity (a
   direct declaration always beats inheritance). Needs its own explicit
   override, at higher specificity (class+element vs Pico's two bare
   elements) to reliably win. Left unfixed, every page link still got
   Pico's square bullet, which — combined with this list running as a
   flex row instead of stacked blocks — visually overlapped the previous
   item instead of sitting in its own reserved marker space. */
.pagination li {
  list-style: none;
}

.pagination li.active a {
  font-weight: bold;
  color: var(--pico-primary);
}

.draft-row {
  font-style: italic;
}

.bare-content {
  max-width: 24rem;
  margin: 4rem auto;
}

.announcement-archive {
  margin-top: 1.5rem;
  font-size: 0.85em;
}

/* Was inheriting the archive block's own 0.85em (meant for the archived
   list itself) plus plain body-text color, making the one thing that
   actually opens the archive the least noticeable line in it. Bumped back
   past 1em (to 1.15em, since 1em alone would still read as same-size-as-
   body) and given the accent color so it reads as the actionable summary
   line it is, not just another archived-entry-sized line of text. */
.announcement-archive summary {
  cursor: pointer;
  font-size: 1.15em;
  font-weight: bold;
  color: var(--pico-primary);
}

.announcement-archive ul {
  list-style: none;
  padding-left: 1rem;
}

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

.field-row {
  margin-bottom: 0.4rem;
}

.field-row-checkbox {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.score-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.score-row-label {
  flex: 1 1 auto;
  min-width: 0;
}

.score-row input[type="range"] {
  flex: 0 0 auto;
  width: 40%;
  margin: 0;
}

.score-row-value {
  flex: 0 0 auto;
  min-width: 2.5ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.field-error {
  display: block;
  color: var(--pico-del-color);
  margin: -0.25rem 0 0.4rem;
}

.field-help {
  display: block;
  color: var(--pico-muted-color);
  margin: -0.25rem 0 0.4rem;
}

/* Flexible row that lets each field's own width class (below) determine
   its size, instead of forcing equal grid columns — fields wrap naturally
   on narrow screens with no separate mobile override needed. Horizontal
   gap is generous (more "horizontal margin" between fields sitting side
   by side); vertical gap (wrapped rows) stays tighter. */
.field-row-inline {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.5rem;
  align-items: flex-start;
}

.field-row-inline .field-row {
  margin-bottom: 0.4rem;
  flex: 0 0 auto;
  min-width: 0;
}

/* Opt-in growth for the one field in a field-row-inline row that should
   actually expand to fill the row (title, next to the compact running_int
   box) -- giving every row flex-grow:1 uniformly split the row's space
   evenly regardless of each field's own max-width, so running_int (capped
   small) still claimed half the row and left a dead gap before title
   started. flex-basis:0% (not auto) is still needed here: Pico's inputs are
   width:100% of their containing block, and a flex item's hypothetical size
   under flex-basis:auto is computed from that child's *intrinsic* size --
   browsers resolve a 100%-wide child's contribution to that calculation as
   if it had no explicit width at all, so without this the field-row would
   size to near its input's unstyled intrinsic width, never reaching the
   input's own max-width class. min-width:0 overrides flex's default
   min-width:auto, which would otherwise still floor that at the same
   intrinsic size. */
.field-row-inline .field-row-grow {
  flex: 1 1 0%;
  min-width: 0;
}

.track-row {
  border-bottom: 1px solid var(--pico-muted-border-color);
  padding-bottom: 0.2rem;
  margin-bottom: 0.4rem;
}

.track-row-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
  align-items: center;
}

.track-row-fields .field-row {
  margin-bottom: 0;
}

.note-toggle {
  margin: 0;
}

.note-toggle summary {
  cursor: pointer;
  list-style: none;
}

.note-toggle summary::-webkit-details-marker {
  display: none;
}

.note-toggle[open] summary {
  margin-bottom: 0.25rem;
}

.remove-row-btn {
  white-space: nowrap;
  margin-bottom: 0;
}

/* Field width classes — sized from real content, not guessed. running_int,
   track artist/title, and ROTW/post title widths come from measuring
   _local_help/ROTW_history-2026-08-08.xlsx (avg + a buffer, not max, since
   a few outlier entries are expected to scroll within the box). Link
   fields are estimated from typical playlist/video URL lengths — the
   spreadsheet's Youtube column was unusable (broken formula export). */
/* Same (class + type) specificity throughout this block on purpose — each
   field carries "compact-input" plus one sizing class, and the later rule
   here needs to win the tie via source order, not a specificity fight. */
input.compact-input,
select.compact-input {
  max-width: 28ch;
}

input.input-int,
select.input-int {
  /* Pico's left+right input padding (2 x --pico-form-element-spacing-
     horizontal, 1rem each) eats ~3.8ch on its own with box-sizing:
     border-box, which is why these ch values look much smaller in the
     browser than the raw number suggests -- doubled from 5ch per explicit
     request, was nearly all padding before. */
  max-width: 10ch;
}

input.input-time,
select.input-time {
  max-width: 11ch;
}

input.input-username,
select.input-username {
  max-width: 24ch;
}

input.input-artist,
select.input-artist {
  max-width: 18ch;
}

input.input-tracktitle,
select.input-tracktitle {
  max-width: 22ch;
}

input.input-posttitle,
select.input-posttitle {
  max-width: 50ch;
}

input.input-link,
select.input-link {
  max-width: 42ch;
}

input.input-note,
select.input-note {
  max-width: 34ch;
}

.list-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1.5rem;
  margin-bottom: 0.85rem;
}

.list-toolbar form {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0;
  flex: 1;
}

/* Pico gives buttons/dropdowns their own default margin, which — as a
   flex sibling of the zero-margin search form above — offsets its visual
   center against align-items:center. Zeroing it here is what actually
   lines the "Create new" button/dropdown up with the search row. */
.list-toolbar > [role=button],
.list-toolbar > .create-dropdown {
  margin: 0;
}

/* Search boxes grow to fill whatever room the toolbar row has; the submit
   button stays sized to its own text ("Search") instead of also trying to
   claim 100% width — Pico's own button[type=submit] rule made it grow
   until it ate all the space, leaving the input squeezed to nothing. */
.list-toolbar form input[type=search] {
  flex: 1;
  margin: 0;
}

.list-toolbar form button[type=submit] {
  flex: 0 0 auto;
  width: fit-content;
  margin: 0;
}

.create-dropdown {
  width: fit-content;
}

/* Pico's dropdown menu is `position:absolute;left:0`, i.e. it always
   grows rightward from the button's left edge, sized to fit its widest
   item ("Unplugged (without playlist)"). That's fine for a dropdown with
   room to its right, but .create-dropdown sits at the far right end of
   the toolbar (.list-toolbar's search form takes the remaining space via
   flex:1), so the menu had nowhere to grow into and ran off the right
   edge of the viewport. Anchoring from the right edge instead makes it
   grow leftward, back over the button/toolbar where there's actual room.
   Selector has to match Pico's own `details.dropdown>summary+ul` shape
   (not just `.create-dropdown > ul`) — that rule's specificity (three
   type selectors + one class) beats a plain class+type selector, so a
   less-specific override was silently losing the cascade regardless of
   stylesheet load order. */
details.create-dropdown > summary + ul {
  left: auto;
  right: 0;
}

.form-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 0.85rem;
}

/* Same reasoning as .topbar .inline-form above: a <form> used only as a
   CSRF+button wrapper (e.g. the "Close poll" button) still carries Pico's
   own default form margin as a flex item, which threw its button off
   against sibling buttons/links in the same row. Removing the form's own
   box promotes the button straight into .form-actions' flex layout. */
.form-actions form {
  display: contents;
}

/* Pico's button[type=submit] defaults to width:100%, which inside a flex
   row like this one made a button compete for space with its siblings —
   at worst shrinking enough that its own label wrapped onto a second
   line, making that one button taller than the rest of the row. This
   caught every plain <button> (Save draft, Publish, Close poll, ...) —
   but an <a role=button> like "Results" isn't a button[type=submit] at
   all, so it kept slipping through unfixed. Both selectors below are
   needed; neither alone covers every case in these rows.

   Pico also gives button[type=submit] its own default margin-bottom
   (~21px at this site's scaled root font-size) that [role=button]
   anchors don't get — with align-items:center that stray margin made the
   button's whole box taller than its siblings', shifting its visible
   content up relative to them even after the width/flex fix above.
   That margin is the actual cause of the reported misalignment; zeroing
   it (not just the width) is what fixes it. */
.form-actions button[type=submit],
.form-actions [role=button] {
  flex: 0 0 auto;
  width: fit-content;
  margin: 0;
}

/* Same width/margin fix, two different reasons depending on which match:
   the account-dropdown's own <summary role=button> is a genuine flex
   child of .account-links (flex:0 0 auto matters there, same as
   .form-actions above); the theme-toggle/Log out buttons nested inside
   the dropdown's menu <li> aren't flex items at all (flex is a no-op for
   them) but still need width:fit-content/margin:0 so they don't stretch
   to Pico's default full-width button + margin-bottom inside that row. */
.topbar .account-links button[type=submit],
.topbar .account-links [role=button] {
  flex: 0 0 auto;
  width: fit-content;
  margin: 0;
}

/* Same off-screen problem and fix as .create-dropdown above — this
   dropdown also sits at the far right edge of its row (the topbar), so
   Pico's default `left:0` growth runs it past the viewport edge; needs
   the same selector shape (not just `.account-dropdown > ul`) to
   actually beat Pico's own higher-specificity rule. */
details.account-dropdown > summary + ul {
  left: auto;
  right: 0;
}

/* Buttons sized to their text instead of Pico's default full-width
   [type=submit] — used where a button sits alone on its own line and
   doesn't need to span the container. */
.btn-fit[type=submit] {
  width: fit-content;
  margin: 0;
}

/* A <button> that needs to stay a real <button> (a POST form submit,
   here — Log out) but should read as plain text, not a bordered/filled
   button — resets every property Pico's own button rule sets, then
   reapplies the exact block/negative-margin/hover-highlight shape Pico
   gives a plain <a> inside a dropdown menu (`details.dropdown>summary+ul
   li a`), so a "Log out" row matches a "profile" link row pixel-for-
   pixel despite being a different element. */
.link-button {
  width: 100%;
  margin: calc(var(--pico-form-element-spacing-vertical) * -0.5) calc(var(--pico-form-element-spacing-horizontal) * -1);
  padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) var(--pico-form-element-spacing-horizontal);
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.link-button:is(:hover, :focus) {
  background-color: var(--pico-dropdown-hover-background-color, var(--pico-secondary-background));
}

.platform-links {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding-left: 0;
  margin: 0.4rem 0 0.85rem;
}

.platform-links li {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.platform-icon {
  height: 1em;
  width: auto;
  vertical-align: middle;
}

.platform-icon.youtube-icon-lg {
  height: 2.5em;
}

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

.post-images img {
  max-width: 320px;
  max-height: 320px;
  border-radius: var(--pico-border-radius);
}

.post-attachments {
  list-style: none;
  padding-left: 0;
  margin: 0.5rem 0;
}

#existing-attachments {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: 0.75rem;
}

.attachment-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.attachment-thumb {
  height: 2.5em;
  width: auto;
  border-radius: var(--pico-border-radius);
}

/* SoundCloud's mark has no brand color of its own (unlike the other three,
   which keep their own logo colors regardless of site theme) — swap the
   black/white variant with the same light/dark switch the rest of the
   site already gets for free from Pico, no JS needed. */
.platform-icon-soundcloud-dark {
  display: none;
}

@media (prefers-color-scheme: dark) {
  .platform-icon-soundcloud-light {
    display: none;
  }

  .platform-icon-soundcloud-dark {
    display: inline;
  }
}

/* Open/closed indicator next to a poll's title. */
.poll-status {
  display: inline-block;
  font-size: 0.55em;
  font-weight: normal;
  vertical-align: middle;
  padding: 0.15em 0.6em;
  border-radius: 1em;
  margin-left: 0.5em;
}

.poll-status-open {
  color: var(--pico-ins-color);
  background: color-mix(in srgb, var(--pico-ins-color) 15%, transparent);
}

.poll-status-closed {
  color: var(--pico-del-color);
  background: color-mix(in srgb, var(--pico-del-color) 15%, transparent);
}

/* Bounded so the radar chart renders at a fixed, readable size instead of
   stretching to fill the content column's full width (which, combined
   with Chart.js's aspect-ratio sizing, made it taller than the viewport
   and forced page scrolling). maintainAspectRatio:false in the chart's
   own JS options lets it fill exactly this box. */
.chart-container {
  width: min(90%, 32rem);
  height: min(65vh, 32rem);
  margin: 0.5rem auto 0;
}

/* Favourite-track highlight in the History table — this color only ever
   lived in the source spreadsheet's cell formatting before; this ties it
   to the site's own accent so it stays consistent if the accent changes. */
.favourite-row td {
  color: var(--pico-primary);
}

/* Alternating row tint — intentionally almost invisible, a resting-state
   scan aid rather than a visible stripe. Explicit rgba per theme rather
   than color-mix(), for broader browser support. */
tbody tr:nth-child(even) {
  background-color: rgba(0, 0, 0, 0.025);
}

@media (prefers-color-scheme: dark) {
  tbody tr:nth-child(even) {
    background-color: rgba(255, 255, 255, 0.035);
  }
}

.login-hero {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background-image: linear-gradient(rgba(10, 10, 12, 0.6), rgba(10, 10, 12, 0.6)), url("blown-away.jpg");
  background-size: cover;
  background-position: center 30%;
}

.login-card {
  width: 100%;
  max-width: 24rem;
  margin: 2rem;
  padding: 2rem;
  background: var(--pico-background-color);
  border-radius: var(--pico-border-radius);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
}

@media (max-width: 768px) {
  .app-shell {
    grid-template-columns: 1fr;
    grid-template-areas:
      "topbar"
      "content";
  }

  .hamburger {
    display: inline-block;
  }

  .sidebar {
    position: fixed;
    /* Anchored below the topbar rather than at the viewport top — the
       topbar's own height isn't a fixed value (it scales with the
       responsive --pico-font-size tiers and its content), so
       topbar-layout.js measures it and sets --topbar-height on page
       load/resize; the 3.5rem fallback only matters if JS is disabled.
       Previously this started flush at top:0, so the sidebar's own first
       nav item ("Announcements") rendered directly underneath/behind the
       topbar's "Notify ROTW" title and close button instead of below
       them. */
    top: var(--topbar-height, 3.5rem);
    left: 0;
    width: 288px;
    height: calc(100vh - var(--topbar-height, 3.5rem));
    /* No background override here (previously a flat var(--pico-
       background-color), which silently dropped the purple tint on
       mobile since this rule comes after — and is equal specificity to
       — the tinted .sidebar rule above) — the tint now carries through
       unchanged from the base/dark-mode rules. */
    box-shadow: 2px 0 8px rgba(0, 0, 0, 0.2);
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    z-index: 100;
  }

  .nav-toggle:checked ~ .sidebar {
    transform: translateX(0);
  }
}
