/* COLOUR THEME */
/* Named palettes. Every page gets one via a "theme-*" class on <body>
   (set in layouts/_default/baseof.html for templated pages, and by hand
   in the standalone tool pages under static/experiments/). To give a
   section its own look later, add it to the theme map in baseof.html —
   no CSS changes needed unless it's a genuinely new palette. */

.theme-light {
  --color-bg: #E8E8E6;
  --color-bg-alt: #FFFFFF;
  --color-bg-deep: #D8D8D5;
  --color-fg: #1A1A1A;
  --color-accent: #B3401F;
  --color-muted: #767676;
  --color-track: #D4D4D2;
  /* Unconnected circles and labels in the homepage canvas (ball.js) only.
     Was --color-hover until 2026-10-05. */
  --color-canvas-secondary: #7BA8D6;
  --color-disabled: #999999;
  --color-code-bg: #1E1E1E;
  --color-panel-border: var(--color-fg);
  --color-input-bg: #FFFFFF;
  /* Native <select> dropdown arrow. A CSS colour can't reach inside a
     data-URI SVG, so each theme carries its own pre-coloured copy instead
     of the arrow being hardcoded once and going invisible under whichever
     theme it wasn't drawn for. */
  --color-select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='%231A1A1A' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}

.theme-dark {
  --color-bg: #153243;
  --color-bg-alt: #0e2230;
  --color-bg-deep: #0a1820;
  --color-fg: #EBF2FA;
  --color-accent: orange;
  --color-muted: #8A9FAD;
  --color-track: #2a4555;
  --color-canvas-secondary: #7D8E9D;
  --color-disabled: #cccccc;
  --color-code-bg: #1E1E1E;
  --color-panel-border: var(--color-fg);
  --color-input-bg: var(--color-bg-deep);
  --color-select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='%23EBF2FA' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}

/* /COLOUR THEME */

/* Live visitor counter (footer widget, populated by /js/live-visitors.js).
   Empty until the fetch resolves, so it never flashes an empty line or a
   stale "0 people" state — see live-visitors.js. Deliberately plain
   body-copy styling (no muted colour, no smaller size), same reasoning as
   #article-live below: reads as an ordinary sentence, not a UI label. */
#live-visitors {
  margin-top: 8px;
}
#live-visitors:empty {
  display: none;
}

/* Per-article live reader count, under the post title (populated by
   /js/live-visitors.js, same empty-hides-cleanly pattern as #live-visitors).
   Deliberately plain body-copy styling (no muted colour, no smaller size)
   so it reads as an ordinary sentence under the title, not a UI label.
   Selector is qualified with `#content .article` to out-specificity the
   general `#content p { margin: ... }` rule below, which would otherwise
   win (it has one more type selector) and cancel out this negative margin
   that pulls the line up under the title's own 24px margin-bottom. */
#content .article #article-live {
  margin: -16px 0 24px 0;
}
#article-live:empty {
  display: none;
}

/* MOBILE */
@media only screen and (max-width: 768px) {
  body {
    margin-top: 32px;
  }
  #header {
	  margin-bottom: 81px;
	  margin-left: 32px;
  }
  #menu {
	  padding: 0;
	  margin-bottom: 81px;
  }
  #content {
  	margin-left: 108px;
		padding-right: 32px;
    position: relative;
    z-index: 1;
  }
  #footer {
    	padding-left: 108px;
  }
  
  #header #logo, #header #description {
    	margin: 0;
    	padding: 0;
  }
  #header #description {
		margin-left: 50px;
  }
  #menuitems {
    margin: 0 0 0 66px;
  }
  #menuitems li {
    text-align: left;
	margin-bottom: 16px;
  }
  .article {
    margin-left: 0;
  }
  #content .article .article-title, #content .article .article-title a {
    margin: 0 0 24px 0;
  }

}

/* TABLET */
@media only screen and (min-width: 769px) and (max-width: 1024px) {
  body {
  margin-top: 81px;
	padding: 0;
  }
  #header {
  	margin-bottom: 81px;
  }
  #menu {
	  margin-bottom: 81px;
  }
  #content {
	  margin-left: 71px;
	  margin-right: 81px;
	  padding-right: 81px;
    position: relative;
    z-index: 1;
  }
  #footer {
    padding-left: 179px;
  }
  #header #logo {
	  margin-left: 81px;
  }
  #header #description {
	  margin-left: 74px;
  }
  #menuitems {
	  margin: 0 0 0 139px; 
    text-align: left;
  }
  #menuitems li {
	  margin-bottom: 16px;
  }
  .article {
    margin-left: 108px;
  }
  #content .article .article-title, #content .article .article-title a {
    margin: 0 0 24px 0;
  }
}

/* DESKTOP */
@media only screen and (min-width: 1025px) {
  body {
    margin-top: 81px;
  }
  #header, #menu, #content, #footer {
    margin: 81px 0;
    padding: 0;
    text-align: left;
  }
  #header {
    margin-bottom: 81px;
    margin-left: 240px;
    margin-top: 81px;
    padding: 0;
  }
  #header #description {
  	margin-left: 82px;
  }
  #menu {
    text-align: right;
    float: left;
    width: 257px;
	  margin: 0;
    padding: 0 90px 0 0;
  }
  #content {
    max-width: 532px;
    float: left;
	margin: 0 0 24px 0;
	padding: 0 0 24px 0;
  position: relative;
  z-index: 1;
  }
  #content.content-wide {
    max-width: 900px;
  }
  #footer {
    width: 100%;
    padding-left: 348px;
    margin-bottom: 81px;
    clear: both;
  }
  
  #menuitems {
	margin: 0;
	padding: 0;
    text-align: right;
	position: relative;
  }
  #menuitems li {
  	margin: 0;
  	padding: 0;
    margin-left: 0;
	margin-bottom: 16px;
  }
  #menuitems a.active, #menuitems a.ancestor {
    position: relative;
    left: 16px;
  }
  .article {
    margin-left: 0;
  }
  #content .article .article-title, #content .article .article-title a {
    margin: 0 0 24px 0;
  }
  #footer {
    padding-left: 348px;
  }
}

/* Logo and Tagline */

#logo {
  color: var(--color-fg);
   font-size: 16px;
   font-weight: 200;
   font-style: normal;
   display: inline;
}
#logo a, #logo a:visited {
    color: var(--color-fg);
    font-size: 16px;
    display: inline;
	text-decoration: none;
}
#logo a:hover {
    color: var(--color-fg);
}

#header #description {
  display: inline;
}

/* LINKS & BUTTONS */

.article a.linktitle {
  color: var(--color-fg) !important;
  border: none !important;
}

.article a, .article a:visited, #content a, #content a:visited {
  text-decoration: none;
  color: var(--color-fg);
  padding: 0 2px;
  border-bottom: 1px dotted var(--color-accent);
}

.article a:hover, #content a:hover {
  background-color: var(--color-bg);
  color: var(--color-fg);
  border-bottom: 1px dotted var(--color-accent);
}

.cta {
  margin: 36px 0;
}
div.cta a {
  padding: 8px !important;
  }
.cta a, .cta a:visited {
  margin-top: 24px;
  background-color: var(--color-bg) !important;
  border: 1px var(--color-fg) dotted !important;
  color: var(--color-fg) !important;
  border-radius: 8px;
  text-decoration: none;
}
.cta a:hover {
  margin-top: 24px;
  background-color: var(--color-fg) !important;
  color: var(--color-bg) !important;
  border: 1px var(--color-fg) solid !important;
  border-radius: 8px;
  text-decoration: none;
}

/* /LINKS & BUTTONS */

#content h1, #content h2 {
  color: var(--color-accent) !important;
}

#content h2.section-label {
  color: var(--color-fg) !important;
}

#content .article .article-title,
#content .article .article-title a,
#content .article .article-title .p-name {
  color: var(--color-accent) !important;
  font-size: 3rem; /* was 4rem (4x body copy); reduced 25% */
  line-height: 1;
}

blockquote {
  background-color: var(--color-fg);
  color: var(--color-bg) !important;
  padding: 12px 16px;
  margin: 0 0 24px 0;
  border-radius: 8px;
}

blockquote * {
  color: var(--color-bg) !important;
}

#content blockquote p:last-child {
  margin-bottom: 0;
}


/* TYPOGRAPHY */

Body * {
    font-family: "Inconsolata", monospace;
    font-optical-sizing: auto;
    font-weight: 200;
    font-style: normal;
	  font-size: 16px;
	  font-variation-settings: "wdth" 100;
	  color: var(--color-fg);
    line-height: 20px;
}

b, strong, th {
  font-weight: inherit;
}

/* MENU */

#menuitems {
  list-style: none;
}
#menuitems li {
  display: block;
}
#menuitems li a {
  color: var(--color-fg);
  text-decoration: none;
}
#menuitems li a span {
  display: none;
}

#menuitems a.disabled {
  color: var(--color-disabled);
}
#menuitems a.active span, #menuitems a.ancestor span {
  display: inline;
}

/* SEARCH */

/* #search-input has no styling of its own anymore - it's wrapped in the same
   .form-field markup #chat-input uses (see list.html), so it picks up the
   same border/radius/padding/font treatment from .form-field input below,
   deliberately, so the Search and Ask inputs look identical rather than two
   subtly different input styles sitting in the same page. */

/* Results flow inline as normal .article entries (same class /posts/ uses per
   post), so they inherit that spacing/typography for free - this only needs to
   style the highlighted match term Meilisearch wraps in <mark>. Split into two
   rules: inside the title it should read as part of the title (same size,
   foreground colour, not orange/bold - orange-on-orange was illegible and the
   browser default <mark> font-size made the matched substring look like a
   different, smaller font mid-word); inside the excerpt it should stand out
   as orange but not bold, matching the rest of the excerpt's weight. */
#search-results h2.article-title mark {
  background: none;
  color: var(--color-fg);
  font-size: inherit;
  font-weight: inherit;
}
#search-results p mark {
  background: none;
  color: var(--color-accent);
  font-weight: normal;
}

/* CHAT (conversational search, /chat.php) */
#chat-answer {
  margin-top: 24px;
}
/* The answer renders in its own .slate-style panel (chat-output) rather
   than a blockquote - a second slate below the input slate, matching the
   CLI Personality experiment's controls-slate + output-slate pairing
   (static/experiments/cli-personality/cli-personality.html), so the Ask
   feature reads like a terminal response rather than an inline quotation.
   Uses the theme's own colour variables (not the article blockquote's
   hardcoded hex) so it holds up in both light and dark mode - the body
   font is already monospace (Inconsolata) sitewide, so no font override
   is needed here for the "CLI" feel. */
.chat-output {
  background: var(--color-bg-alt);
  border: 1px dotted var(--color-panel-border);
  border-radius: 8px;
  padding: 26px 26px 20px;
  margin-bottom: 24px;
}
.chat-answer-text {
  margin: 0 0 16px 0;
  color: var(--color-fg);
  white-space: pre-wrap;
}
.chat-answer-text:last-child {
  margin-bottom: 0;
}
.chat-sources {
  color: var(--color-muted);
  font-size: 14px;
}
.chat-sources a {
  color: var(--color-muted);
  text-decoration: underline;
}
.chat-sources a:hover {
  color: var(--color-fg);
}
/* Related-question links (see chat.js's renderRelated) - styled to match
   .chat-sources ("I looked at: ...") just above rather than as buttons,
   since both are "here's where else to go" lines. Real <button> elements
   under the hood (each fires a fresh ask(), not a navigation), stripped
   back to look exactly like the muted underlined text .chat-sources a
   already uses. */
.chat-related {
  margin-top: 16px;
  color: var(--color-muted);
  font-size: 14px;
}
.chat-related-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--color-muted);
  text-decoration: underline;
  cursor: pointer;
}
.chat-related-btn:hover {
  color: var(--color-fg);
}
.chat-error {
  background-color: var(--color-bg-alt);
  border: 1px dotted var(--color-panel-border);
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 0;
  color: var(--color-accent);
}
/* Ask button + Speech toggle share a row rather than stacking as separate
   .form-field blocks - one control group, not two. */
.chat-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
/* Speech toggle is a second .form-submit button rather than a checkbox -
   reads the answer aloud via SAM (static/experiments/sam/sam.html's
   engine, loaded on this page too - see list.html) at a fixed voice
   setting rather than exposing sliders here. Its label text flips between
   "Speech off"/"Speech on" in chat.js; this just gives the "on" state
   (aria-pressed="true") the same inverted look .form-submit already uses
   on hover, so it visibly stays lit while active. */
#speech-toggle[aria-pressed="true"] {
  background-color: var(--color-fg) !important;
  color: var(--color-bg) !important;
  border-color: var(--color-fg) !important;
}
/* Pushed to the far right of .chat-controls regardless of whether the Ask
   button next to it is visible - see chat.js's updateAskButtonVisibility,
   which hides Ask until the field has text. */
#speech-toggle {
  margin-left: auto;
}
/* /CHAT */

/* DISCLAIMER */
/* A small provenance/context note a post can surface near the top, e.g.
   flagging AI-assisted drafting (see the disclaimer shortcode). Its own box
   rather than .slate, which is reserved for interactive form controls -
   this should read as a quiet aside, not another input panel. */
.disclaimer {
  /* Same inverted swap as blockquote below (background = --color-fg,
     text = --color-bg) - light box, dark blue text on the site's dark
     theme, no border. Kept as its own rule rather than reusing
     `blockquote` directly, since this isn't a quotation. */
  background-color: var(--color-fg);
  color: var(--color-bg) !important;
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 24px;
}
.disclaimer p:last-child {
  margin-bottom: 0;
}
/* /DISCLAIMER */

/* Remaining Styles */


body {
  background: var(--color-bg);
}

/* CONTENT */
#content .article {
  margin-bottom: 81px;
}
#content p {
  margin-bottom: 24px;
  margin-top: 0;
  clear: both;
}

#content ol, #content ul:not(.tags) {
  margin: 0 0 24px 0;
  padding-left: 1.5em;
}
#content li {
  margin: 0;
  padding: 0;
  line-height: 20px;
}

#content .article time {
  display: block;
  margin-bottom: 24px;
}

#content .article img {
  margin-bottom: 24px;
}
ul.tags {
  margin: 0 0 24px 0;
  padding: 0 !important;
  clear: both;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
ul.tags li {
  list-style: none;
  border: 1px dotted var(--color-fg);
  padding: 2px 4px !important;
  border-radius: 8px;
  background-color: var(--color-bg) !important;
  line-height: normal !important;
}
a.tag {
	text-decoration: none;
  border-bottom: none;
  color: var(--color-fg) !important;
  background-color: var(--color-bg) !important;
  border: none !important;
  margin-bottom: 24px !important;
}
a.tag:hover {
	text-decoration: none;
  color: var(--color-fg) !important;
  background-color: var(--color-bg) !important;
  border: none !important;
}

#canvas-container canvas {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  pointer-events: none; /* Prevents the canvas from interfering with content */
}

.article img {
    display: block;
    width: 100%;
    height: auto;
    filter: grayscale(100%);
  }

.youtube-embed {
  margin-bottom: 24px;
}

  #CLI {
    max-width: 100%; /* Ensures the div does not exceed the width of the viewport */
    word-wrap: break-word; /* Allows breaking of long words if necessary */
    white-space: normal; /* Ensures normal wrapping behavior */
    overflow-wrap: break-word; /* Provides wrapping in certain browsers */
    line-height: 1.5; /* Improves readability with spacing between lines */
    background-color: var(--color-code-bg);
    padding: 8px 16px;
    border-radius: 8px;
    margin-bottom: 24px;
}
#CLI p {
  color: var(--color-fg) !important;
}

/********** Range Input Styles **********/
/* Matches the audio player's progress/volume slider: thin filled track, small dot thumb */
/*Range Reset*/
/* Touch targets: the input is 32px tall and the thumb is a 32px circle
   with a 9px transparent border, so it looks 14px but is 32px to a finger
   (mobile Safari struggled with the bare 14px thumb). The negative margins
   and middle alignment keep the slider taking the same layout space it did
   at 6px tall (within a pixel), so nothing around it moves. */
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: pointer;
  width: 15rem;
  height: 32px;
  margin: -6.5px 0;
  vertical-align: middle;
  --range-progress: 0%;
  touch-action: pan-y;
}

/* Removes default focus */
input[type="range"]:focus {
  outline: none;
}

/***** Chrome, Safari, Opera, and Edge Chromium styles *****/
/* slider track, filled up to the thumb's centre via --range-progress and
   --range-fraction (set by /js/range-fill.js). The thumb's centre runs from
   16px to width - 16px, hence the calc. */
input[type="range"]::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(to right,
    var(--color-accent) calc(var(--range-progress, 0%) + 16px - 32px * var(--range-fraction, 0)),
    var(--color-track) calc(var(--range-progress, 0%) + 16px - 32px * var(--range-fraction, 0)));
}

/* slider thumb */
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; /* Override default look */
  appearance: none;
  margin-top: -13px; /* Centres the 32px hit area on the 6px track */

  /*custom styles*/
  box-sizing: border-box;
  height: 32px;
  width: 32px;
  border: 9px solid transparent; /* hit area; only the 14px centre is drawn */
  background-color: var(--color-fg);
  background-clip: content-box;
  border-radius: 50%; /* Makes the thumb a circle */
}

input[type="range"]:focus::-webkit-slider-thumb {
  outline: 2px solid var(--color-accent);
  outline-offset: -7px; /* 2px outside the visible 14px circle */
}

/******** Firefox styles ********/
/* slider track (base, unfilled) */
input[type="range"]::-moz-range-track {
  background-color: var(--color-track);
  border-radius: 3px;
  height: 6px;
}

/* fills natively up to the current value, no JS needed */
input[type="range"]::-moz-range-progress {
  background-color: var(--color-accent);
  border-radius: 3px;
  height: 6px;
}

/* slider thumb */
input[type="range"]::-moz-range-thumb {
  box-sizing: border-box;
  border: 9px solid transparent; /* hit area, as above */
  border-radius: 50%; /* Makes the thumb a circle */

  /*custom styles*/
  background-color: var(--color-fg);
  background-clip: content-box;
  height: 32px;
  width: 32px;
}

input[type="range"]:focus::-moz-range-thumb {
  outline: 2px solid var(--color-accent);
  outline-offset: -7px;
}

/* Inside a panel, a slider spans the panel's width rather than stopping at
   15rem, so a stack of controls reads as one block. Tools that lay sliders
   out differently (SAM's label-beside-slider rows) override this in their
   own tool.css, which loads after main.css. */
.slate input[type="range"] {
  width: 100%;
}

/* AUDIO PLAYER */

.audio-player {
  margin: 24px 0;
  background-color: var(--color-bg-alt);
  border: 1px dotted var(--color-panel-border);
  border-radius: 8px;
  padding: 16px;
  width: 100%;
  box-sizing: border-box;
}
.audio-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-width: 0;
  line-height: 0;
}
.audio-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 8px 0;
}
.audio-title {
  font-size: 14px;
  color: var(--color-fg) !important;
  margin: 0;
}
.audio-title a, .audio-title a:visited {
  color: var(--color-fg) !important;
  border-bottom: 1px dotted var(--color-accent);
  background-color: transparent !important;
  padding: 0 !important;
}
.audio-title a:hover {
  background-color: transparent !important;
  color: var(--color-accent) !important;
  border-bottom: 1px dotted var(--color-accent);
  padding: 0 !important;
}
.audio-btn {
  background-color: var(--color-bg);
  color: var(--color-fg) !important;
  border: 1px dotted var(--color-fg);
  border-radius: 16px;
  padding: 2px 10px;
  cursor: pointer;
  flex-shrink: 0;
  font-size: 16px;
  font-family: "Inconsolata", monospace;
  -webkit-appearance: none;
  appearance: none;
  display: flex;
  align-items: center;
  line-height: 1;
}
.audio-btn:hover,
.audio-btn:hover * {
  background-color: var(--color-fg);
  color: var(--color-bg) !important;
  border-color: var(--color-bg);
}
.audio-progress-wrap {
  flex: 1;
  min-width: 0;
  height: 6px;
  background-color: var(--color-track);
  border-radius: 3px;
  cursor: pointer;
  position: relative;
  overflow: visible;
  margin-bottom: 20px;
}
.audio-progress-bar {
  height: 100%;
  width: 0%;
  background-color: var(--color-accent);
  border-radius: 3px;
  pointer-events: none;
  position: relative;
}
.audio-thumb {
  width: 14px;
  height: 14px;
  background-color: var(--color-fg);
  border-radius: 50%;
  position: absolute;
  right: -7px;
  top: -4px;
  pointer-events: none;
}
.audio-time {
  font-size: 16px;
  color: var(--color-fg) !important;
  white-space: nowrap;
  min-width: 40px;
  margin-bottom: 20px;
}

.audio-volume-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  line-height: 0;
}
.audio-volume-label {
  font-size: 16px;
  color: var(--color-fg) !important;
  flex-shrink: 0;
}
.audio-volume-wrap {
  width: 80px;
  flex-shrink: 0;
  margin-right: 7px;
  height: 6px;
  background-color: var(--color-track);
  border-radius: 3px;
  cursor: pointer;
  position: relative;
  overflow: visible;
}
.audio-volume-bar {
  height: 100%;
  width: 100%;
  background-color: var(--color-accent);
  border-radius: 3px;
  pointer-events: none;
  position: relative;
}

/* Bigger touch targets for the hand-drawn scrubbers: an invisible strip
   12px above and below each bar that takes the same clicks/taps (the seek
   maths only uses the x position, so the extra height is harmless). */
.audio-progress-wrap::before,
.audio-volume-wrap::before,
.release-progress-wrap::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -12px;
  bottom: -12px;
}

/* /AUDIO PLAYER */

/* EQ VISUALIZER */

.eq-player {
  margin: 24px 0;
  background-color: var(--color-bg-alt);
  border: 1px dotted var(--color-panel-border);
  border-radius: 8px;
  padding: 16px;
  max-width: 100%;
  overflow: hidden;
  box-sizing: border-box;
}
.eq-label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-muted);
  margin-bottom: 10px;
}
.eq-canvas {
  display: block;
  position: static;
  width: auto;
  border-radius: 4px;
}

.eq-canvas--stacked {
  margin-top: 16px;
}

/* /EQ VISUALIZER */

/* RELEASE PLAYER */

.release-player {
  margin: 24px 0;
  background-color: var(--color-bg-alt);
  border: 1px dotted var(--color-panel-border);
  border-radius: 8px;
  overflow: hidden;
  box-sizing: border-box;
}

.release-vinyl-wrap {
  padding: 16px;
  display: flex;
  justify-content: center;
}

.release-vinyl-disc {
  width: 100%;
  aspect-ratio: 1;
  transform-origin: center center;
}

.release-vinyl-disc svg {
  width: 100%;
  height: 100%;
  display: block;
}

.release-vinyl--spinning {
  animation: release-spin 2.667s linear infinite;
}

@keyframes release-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.release-info {
  padding: 0 16px 16px;
}

.release-meta {
  font-size: 14px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-fg);
  margin-bottom: 6px;
  padding-left: 30px;
}

.release-title {
  font-size: 14px;
  color: var(--color-fg);
  margin-bottom: 16px;
  padding-left: 30px;
}

.release-tracklist {
  list-style: none;
  margin: 0 0 4px 0;
  padding: 0;
}

.release-track {
  display: block;
  padding: 8px 0;
  border-bottom: 1px dotted color-mix(in srgb, var(--color-fg) 10%, transparent);
  cursor: pointer;
}

.release-track:last-child {
  border-bottom: none;
}

.release-track-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  color: var(--color-fg);
}

.release-track:hover .release-track-row,
.release-track.is-playing .release-track-row {
  color: var(--color-accent);
}

.track-num {
  width: 18px;
  flex-shrink: 0;
  color: var(--color-fg);
  font-size: 14px;
  text-align: right;
}

/* Hover arrow stays white; playing arrow goes orange */
.release-track:hover .track-num { color: var(--color-fg); }
.release-track.is-playing .track-num { color: var(--color-accent); }

/* Duration: in row when not playing, in progress bar when playing */
.track-dur--bar { display: none; }
.release-track.is-playing .track-dur--row { display: none; }
.release-track.is-playing .track-dur--bar { display: block; }

.track-name {
  flex: 1;
  font-size: 14px;
}

.track-dur {
  font-size: 14px;
  color: var(--color-fg);
  flex-shrink: 0;
  margin-left: auto;
}

.release-track-bottom {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 6px 0 0 30px;
}

.release-track.is-playing .release-track-bottom {
  margin-bottom: 8px;
}

.release-current-time {
  display: none;
  font-size: 14px;
  color: var(--color-fg);
  flex-shrink: 0;
  width: 32px;
}

.release-track.is-playing .release-current-time {
  display: block;
}

.release-progress-wrap {
  display: none;
  flex: 1;
  height: 4px;
  background: var(--color-track);
  border-radius: 2px;
  cursor: pointer;
  position: relative;
  overflow: visible;
}

.release-track.is-playing .release-progress-wrap {
  display: block;
}

.release-progress-bar {
  height: 100%;
  width: 0%;
  background: var(--color-accent);
  border-radius: 2px;
  pointer-events: none;
  position: relative;
}

.release-progress-thumb {
  width: 12px;
  height: 12px;
  background: var(--color-fg);
  border-radius: 50%;
  position: absolute;
  right: -6px;
  top: -4px;
  pointer-events: none;
}

.release-desc {
  font-size: 14px;
  color: var(--color-muted);
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px dotted color-mix(in srgb, var(--color-fg) 10%, transparent);
}

.release-desc p {
  margin-bottom: 0;
}

/* /RELEASE PLAYER */

/* Blinking cursor effect */
.cursor {
  display: inline-block;
  width: 2px;
  height: 1em;
  background-color: var(--color-fg);
  animation: blink 0.7s infinite;
  vertical-align: middle;
}

@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
.controls {
  margin-bottom: 24px;
}
button {
height: 32px;
padding: 0 24px;
margin: 0 0 24px 0;
background-color:var(--color-bg);
border: 1px dotted var(--color-fg);
color: var(--color-fg);
border-radius: 8px;
}
button.primary {
  background-color: var(--color-fg);
  border: 1px solid var(--color-fg);
  color: var(--color-bg);
}

/* CONTACT FORM */
.slate {
  background: var(--color-bg-alt);
  border: 1px dotted var(--color-panel-border);
  border-radius: 8px;
  padding: 26px 26px 20px;
  margin-bottom: 24px;
}

.form-field {
  margin-bottom: 24px;
}
.form-field:last-child {
  margin-bottom: 0;
}
.form-field label {
  display: block;
  margin-bottom: 8px;
  color: var(--color-fg);
}
/* /posts/ search field only - Ben asked for the space below the "Search"
   label to match the space below the field itself (.form-field's own
   24px margin-bottom), rather than the standard 8px used everywhere else
   (contact form, Ask box). Scoped to .posts-search-field so it doesn't
   change spacing on those other forms. */
.posts-search-field label {
  margin-bottom: 24px;
}
.posts-search-field input {
  background-color: var(--color-bg) !important;
}
.posts-search-field input::placeholder {
  color: var(--color-fg) !important;
}

/* Tag facet pills above the /posts/ search results (#search-facets, built
   by static/js/search.js from Meilisearch's facetDistribution.tags). Same
   dotted-border/rounded-corner
   language as the site's buttons and the Speech toggle, at a smaller size
   since these sit inline in a row rather than acting as a single form
   control; the active pill mirrors #speech-toggle[aria-pressed="true"]. */
.facet-pills {
  margin-bottom: 24px;
}
.facet-pill {
  display: inline-block;
  height: auto;
  padding: 4px 12px;
  margin: 0 8px 8px 0;
  background-color: var(--color-bg);
  border: 1px dotted var(--color-fg);
  border-radius: 8px;
  color: var(--color-fg);
  font-family: "Inconsolata", monospace;
  font-size: 14px;
  font-weight: 200;
  cursor: pointer;
}
.facet-pill:hover {
  border-color: var(--color-accent);
}
.facet-pill[aria-pressed="true"] {
  background-color: var(--color-fg);
  color: var(--color-bg);
  border-color: var(--color-fg);
}
.form-field input,
.form-field textarea {
  width: 100%;
  box-sizing: border-box;
  background-color: var(--color-input-bg);
  border: 1px dotted var(--color-panel-border);
  border-radius: 8px;
  padding: 8px 12px;
  color: var(--color-fg);
  font-family: "Inconsolata", monospace;
  font-size: 16px;
  font-weight: 200;
  -webkit-appearance: none;
  appearance: none;
}
.form-field input::placeholder,
.form-field textarea::placeholder {
  color: var(--color-muted);
}
.form-field input:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--color-accent);
}
.form-field textarea {
  resize: vertical;
}
/* The browser's own "clear" (x) button inside <input type="search">
   (Chrome/Safari only - Firefox doesn't render one) is a small grey/blue
   system icon, not stylable via `color` - first replaced with a white x
   via an SVG mask, then replaced again with a proper "Clear" text button
   below (search-clear-btn) since that's clearer and works in Firefox too.
   Hidden outright here rather than left showing alongside the new button.
   Scoped to .form-field so it covers the search box wherever it appears
   (/posts/ and /search/). */
.form-field input[type="search"]::-webkit-search-cancel-button {
  display: none;
}
/* Positions search-clear-btn over the right edge of the input - needs its
   own wrapper (rather than .form-field itself) so the label above stays
   in the normal block flow instead of being pulled into a relative/flex
   context meant only for the input. */
.search-input-wrap {
  position: relative;
}
.search-input-wrap input[type="search"] {
  padding-right: 64px;
}
.search-clear-btn {
  position: absolute;
  top: 50%;
  right: 12px;
  transform: translateY(-50%);
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-size: 14px;
  color: var(--color-fg);
  text-decoration: none;
  cursor: pointer;
}
.search-clear-btn:hover {
  color: var(--color-accent);
}

/* "Did you mean X?" hint under the search field (#search-suggestion), shown
   when Meilisearch's typo tolerance quietly corrected what was typed to
   find a match - see static/js/search.js's renderSuggestion() for how the
   corrected word is worked out. Plain muted text with the suggested word
   itself as the one clickable, actionable part - underlined/accent-coloured
   (unlike .search-clear-btn, which was deliberately made to look like plain
   text rather than a link) since this is closer to a hyperlink prompting a
   specific action than a form control. */
.search-suggestion {
  margin: -8px 0 24px;
  color: var(--color-fg);
}
.search-suggestion-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-size: inherit;
  color: var(--color-accent);
  text-decoration: underline;
  cursor: pointer;
}
.search-suggestion-btn:hover {
  color: var(--color-fg);
}
.form-submit {
  margin: 0 !important;
  background-color: var(--color-bg) !important;
  color: var(--color-fg) !important;
  border: 1px dotted var(--color-fg) !important;
  border-radius: 8px;
  padding: 8px 24px !important;
  height: auto !important;
  cursor: pointer;
  font-family: "Inconsolata", monospace;
  font-size: 16px;
  font-weight: 200;
}
.form-submit:hover {
  background-color: var(--color-fg) !important;
  color: var(--color-bg) !important;
  border-color: var(--color-fg) !important;
}
table.social-table {
  border-collapse: collapse;
  width: 100%;
  margin: 24px 0 0;
  table-layout: fixed;
}
table.social-table th:first-child,
table.social-table td:first-child {
  width: 110px;
}
table.social-table th {
  text-align: left;
  color: var(--color-fg);
  padding: 0 10px 10px 0;
}
table.social-table td {
  border-top: 1px dotted var(--color-muted);
  padding: 8px 14px 8px 0;
  vertical-align: top;
}
/* Error stays in its own boxed, accent-coloured panel - unchanged. */
[data-contact-error] {
  background-color: var(--color-bg-alt) !important;
  border: 1px dotted var(--color-panel-border) !important;
  border-radius: 8px !important;
  padding: 12px 16px !important;
  margin-bottom: 24px !important;
  color: var(--color-accent) !important;
}
[data-contact-error] * {
  color: var(--color-accent) !important;
  background-color: transparent !important;
}
/* Success is plain white text with no containing box - removed
   2026-09-22 at Ben's request, split out from the shared error styling
   above so the two could diverge. Uses --color-fg (was a hardcoded
   #FFFFFF until 2026-10-05) so it holds up in the light theme too. */
[data-contact-success] {
  margin-bottom: 24px !important;
  color: var(--color-fg) !important;
}
[data-contact-success] * {
  color: var(--color-fg) !important;
  background-color: transparent !important;
}

/* Conversational contact form (contact-chat.js) - the running transcript
   reuses .chat-answer-text wholesale (see the CHAT section above) for the
   assistant's own lines' text styling, but NOT .chat-output's panel
   chrome - #contact-chat-panel deliberately has no border/background of
   its own (removed 2026-09-22 at Ben's request), so the whole widget
   just flows as plain page content rather than sitting in a boxed
   "terminal response" panel the way the Ask feature's single answer
   does. margin-bottom here replaces the spacing .chat-output's own
   margin-bottom used to provide, so the Social table below doesn't end
   up flush against the last chat line. */
#contact-chat-panel {
  margin-bottom: 24px;
}
.contact-chat-user {
  margin: 0 0 16px 0;
  color: var(--color-accent);
}
.contact-chat-user:last-child {
  margin-bottom: 0;
}
/* Spacing between the transcript and the input row below it - no border
   or background of its own (see #contact-chat-panel above), just the
   breathing room .slate's own padding used to provide back when the
   input lived in a separate boxed panel. */
.contact-chat-input-row {
  margin-top: 24px;
}
/* The "send this" button shown once contact-chat.php's READY: sentinel
   comes back (see its header comment) - just a quiet divider under the
   assistant's own reply (which already recaps name/email/message in
   plain text, so this doesn't repeat them - see contact-chat.js) and the
   one button that actually sends. Sits inside #contact-chat-log itself
   (appended by contact-chat.js), not a separate panel, so it reads as
   part of the same conversation rather than a second, disconnected
   form. */
.contact-chat-ready {
  margin-top: 8px;
  padding-top: 16px;
  border-top: 1px dotted var(--color-panel-border);
}
.contact-chat-actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}
/* /CONTACT FORM */
/* KEYBOARD FOCUS */
/* One focus treatment for everything you can tab to: a dotted accent
   outline, echoing the house dotted border. :focus-visible means it shows
   for keyboard focus only, not on mouse clicks. Form inputs keep their
   accent border too. Range thumbs have their own outline (see Range Input
   Styles). */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
.form-field input:focus-visible,
.form-field textarea:focus-visible,
select:focus-visible {
  outline: 2px dotted var(--color-accent);
  outline-offset: 2px;
}

/* REDUCED MOTION */
/* Visitors who ask their system for less motion get no spinning record and
   no homepage/404 background animation (ball.js also skips drawing it).
   The typing effects check the same setting in typing.js. */
@media (prefers-reduced-motion: reduce) {
  .release-vinyl--spinning {
    animation: none;
  }
  #canvas-container {
    display: none;
  }
}
