/*
Theme Name: EchoNest Recording
Theme URI: https://echonestrecording.com
Author: EchoNest Recording
Description: Content site for EchoNest Recording Studio — Koreatown, Los Angeles. Warm dark theme with amber accents, ported from the studio's React design. Pairs with the React booking app served at /book.
Version: 4.0.0
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: echonest
*/

/* ==========================================================================
   Design tokens and layout are ported verbatim from wordpress/preview/styles.css,
   which Randy reviewed and approved as a faithful match for the base44 design.
   The Google Fonts @import that lived at the top of that file is intentionally gone —
   functions.php enqueues the same three families properly instead.
   ========================================================================== */


:root {
  --background: hsl(30 5% 6%);
  --foreground: hsl(40 20% 95%);
  --card: hsl(30 5% 9%);
  --primary: hsl(38 90% 55%);
  --primary-foreground: hsl(30 5% 6%);
  --secondary: hsl(30 5% 14%);
  --secondary-foreground: hsl(40 15% 80%);
  --muted-foreground: hsl(30 10% 50%);
  --border: hsl(30 8% 18%);
  --radius: 0.75rem;
  /* The second accent, added 2026-08-04. Randy: *"i wouldn't mind one other color coming in so it
     feels like something exciting happened."* Violet rather than an arbitrary contrast colour — it
     is the neon already in the room in the Preet shoot, so it belongs to the brand instead of being
     invented for a menu.

     IT HAS EXACTLY ONE JOB: MEMBERSHIP. Amber is booking and is the site's only CTA colour; violet
     marks the paid tier and the upgrade path. That split is why the Upgrade row can sit a few inches
     below an amber Book Now without the two competing. Do not spend this colour on anything else —
     a second accent used twice for different reasons is just a third palette. */
  --accent: hsl(288 60% 66%);
  --font-sans: 'Inter', sans-serif;
  --font-display: 'Playfair Display', serif;
  --font-brand: 'Big Shoulders Display', sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

/* ---- layout helpers ---- */
.wrap-5xl { max-width: 64rem; margin: 0 auto; }
.wrap-3xl { max-width: 48rem; margin: 0 auto; }
.wrap-6xl { max-width: 72rem; margin: 0 auto; }
/* Wider than every other column on the site, and used once: the signal-chain feature on the home
   page. 72rem is where the header's wordmark starts, so a block capped there can never look bigger
   than the page — this one runs to the section's gutters and caps at 96rem so its edges sit outside
   the wordmark's. Do not reach for it for anything else; two "widest" blocks is neither. */
.wrap-bleed { max-width: 100rem; margin: 0 auto; }
.px { padding-left: 1.5rem; padding-right: 1.5rem; }
.text-primary { color: var(--primary); }
.text-muted { color: var(--muted-foreground); }
.eyebrow { font-size: .75rem; text-transform: uppercase; letter-spacing: .25em; color: var(--primary); font-weight: 500; }

/* ---- buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  font-weight: 600; font-size: 1rem; border-radius: 9999px; cursor: pointer;
  padding: 1rem 2rem; transition: all .2s ease; border: 1px solid transparent;
}
.btn-primary { background: var(--primary); color: var(--primary-foreground); box-shadow: 0 10px 25px -5px hsl(38 90% 55% / .25); }
.btn-primary:hover { background: hsl(38 90% 50%); }
.btn-outline { background: transparent; border-color: var(--border); color: var(--foreground); }
.btn-outline:hover { background: var(--secondary); }
.btn-lg { padding: 1.1rem 2.5rem; }

/* ---- top nav (About / Contact / Return Policy) ---- */
.topbar { border-bottom: 1px solid hsl(30 8% 18% / .6); padding: 1rem 1.5rem; }
.topbar .inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.topbar .brand-word { font-family: var(--font-brand); font-size: 1.5rem; font-weight: 700; letter-spacing: .08em; }
.topbar img.logo { height: 51px; width: auto; }
.topbar nav { display: flex; align-items: center; gap: 1.5rem; }
.topbar nav a { font-size: .875rem; color: var(--muted-foreground); transition: color .2s; }
.topbar nav a:hover { color: var(--primary); }
.topbar nav a.active { color: var(--primary); font-weight: 500; }

/* ---- hero (Home) ---- */
.hero { position: relative; min-height: 100vh; display: flex; align-items: center; justify-content: center; overflow: hidden; text-align: center; }
.hero .bg { position: absolute; inset: 0; z-index: 0; }
.hero .bg img { width: 100%; height: 100%; object-fit: cover; }
.hero .bg::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to bottom, hsl(30 5% 6% / .9), hsl(30 5% 6% / .7), hsl(30 5% 6%)); }
.hero .content { position: relative; z-index: 10; max-width: 64rem; padding: 4rem 1.5rem; }
.hero img.logo { height: 200px; width: auto; margin: 0 auto .5rem; border-radius: .5rem; }
.pill { display: inline-flex; align-items: center; gap: .5rem; padding: .5rem 1rem; border-radius: 9999px; border: 1px solid hsl(38 90% 55% / .3); background: hsl(38 90% 55% / .1); margin-bottom: 2rem; }
.pill .dot { width: .5rem; height: .5rem; border-radius: 9999px; background: var(--primary); }
.pill span { font-size: .875rem; font-weight: 500; color: var(--primary); }
.hero h1 { font-family: var(--font-display); font-weight: 700; letter-spacing: -.02em; line-height: 1.1; margin-bottom: 1.5rem; font-size: clamp(3rem, 8vw, 6rem); }
.hero p.lead { font-size: 1.125rem; color: var(--muted-foreground); max-width: 42rem; margin: 0 auto 2.5rem; }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 1rem; margin-bottom: 4rem; }
.feature-grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; max-width: 48rem; margin: 0 auto; }
.feature { display: flex; flex-direction: column; align-items: center; gap: .75rem; padding: 1.5rem; border-radius: 1rem; background: hsl(30 5% 9% / .6); border: 1px solid hsl(30 8% 18% / .5); backdrop-filter: blur(4px); }
.feature .ico { width: 3rem; height: 3rem; border-radius: .75rem; background: hsl(38 90% 55% / .1); display: flex; align-items: center; justify-content: center; font-size: 1.4rem; }
.feature .label { font-weight: 600; font-size: .875rem; }
.feature .desc { font-size: .75rem; color: var(--muted-foreground); margin-top: .25rem; }

/* ---- section headings ---- */
.section { padding: 6rem 1.5rem; }
.section h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.25rem, 5vw, 3rem); margin-bottom: 1rem; }
.section .sub { color: var(--muted-foreground); font-size: 1.125rem; max-width: 36rem; margin: 0 auto; }
.center { text-align: center; }

/* ---- pricing ---- */

/* (The .rate-notice grand-opening block lived here. Removed round 8, 2026-09-02 — the promotion is
   over. It was always marked temporary; nothing replaces it.) */

.price-grid { display: grid; grid-template-columns: 1fr; gap: 2rem; max-width: 48rem; margin: 4rem auto 0; }
.price-card { position: relative; padding: 2rem; border-radius: 1.5rem; border: 1px solid hsl(30 8% 18% / .6); background: var(--card); transition: border-color .2s; }
.price-card:hover { border-color: hsl(38 90% 55% / .4); }
.price-card.featured { border: 2px solid hsl(38 90% 55% / .6); box-shadow: 0 10px 25px -5px hsl(38 90% 55% / .1); }
.price-card .tier { font-size: .875rem; font-weight: 500; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: .05em; margin-bottom: .5rem; }
.price-card.featured .tier { color: var(--primary); }
.price-card .amount { font-size: 3rem; font-weight: 700; color: var(--primary); line-height: 1; }
.price-card .per { color: var(--muted-foreground); }
.price-card .range { font-size: .875rem; color: var(--muted-foreground); margin-top: .5rem; }
.price-feats { margin: 1.5rem 0 2rem; display: flex; flex-direction: column; gap: .75rem; }
.price-feats div { display: flex; align-items: center; gap: .75rem; font-size: .875rem; color: var(--secondary-foreground); }
.price-feats .chk { color: var(--primary); font-weight: 700; }
.badge-best { position: absolute; top: -.75rem; left: 2rem; display: inline-flex; align-items: center; gap: .25rem; padding: .25rem .75rem; background: var(--primary); color: var(--primary-foreground); border-radius: 9999px; font-size: .75rem; font-weight: 700; }
.btn-block { width: 100%; padding: 1.1rem; }
.eng-addon { margin: 2rem auto 0; max-width: 48rem; padding: 1.5rem; border-radius: 1rem; border: 1px solid hsl(30 8% 18% / .6); background: hsl(30 5% 9% / .5); display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; }
.eng-addon .left { display: flex; align-items: center; gap: 1rem; }
.eng-addon .ico { width: 3rem; height: 3rem; border-radius: .75rem; background: hsl(38 90% 55% / .1); display: flex; align-items: center; justify-content: center; font-size: 1.25rem; }
.eng-addon .price { font-size: 1.5rem; font-weight: 700; color: var(--primary); }
.eng-addon .price small { font-size: .875rem; color: var(--muted-foreground); font-weight: 400; }

/* ---- CTA ---- */
.cta { padding: 6rem 1.5rem; text-align: center; }
.cta h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.25rem, 5vw, 3rem); margin-bottom: 1rem; }
.cta p { color: var(--muted-foreground); font-size: 1.125rem; margin: 0 auto 2rem; max-width: 32rem; }

/* ---- footer ---- */
footer.site { border-top: 1px solid hsl(30 8% 18% / .4); padding: 2.5rem 1.5rem; }
footer.site .cols { display: flex; flex-direction: column; align-items: center; gap: 1.5rem; text-align: center; }
footer.site img.logo { height: 51px; width: auto; margin: 0 auto .25rem; }
footer.site .addr { font-size: .875rem; color: var(--muted-foreground); }
/* The Instagram link, sized to the address block it sits under rather than to the icon — it is a
   footer detail, not a call to action, and the whole row must not out-weigh the phone number above
   it. Centred with the column on a phone, left-aligned beside the logo above 768 (see .cols). */
footer.site .social {
  display: inline-flex; align-items: center; gap: .45rem; margin-top: .6rem;
  font-size: .875rem; color: var(--muted-foreground); transition: color .2s;
}
footer.site .social:hover { color: var(--primary); }
footer.site .social svg { width: 1.05rem; height: 1.05rem; flex: none; }

footer.site .links { display: flex; align-items: center; gap: 1.5rem; }
footer.site .links a { font-size: .875rem; color: var(--muted-foreground); transition: color .2s; }
footer.site .links a:hover { color: var(--foreground); }
footer.site .copy { max-width: 64rem; margin: 1.5rem auto 0; padding-top: 1.5rem; border-top: 1px solid hsl(30 8% 18% / .3); text-align: center; font-size: .75rem; color: var(--muted-foreground); }
.footer-mini { border-top: 1px solid hsl(30 8% 18% / .4); padding: 2rem 1.5rem; text-align: center; font-size: .75rem; color: var(--muted-foreground); }

/* ---- About ---- */
.about-hero { position: relative; height: 40vh; min-height: 280px; overflow: hidden; }
.about-hero img { width: 100%; height: 100%; object-fit: cover; }
.about-hero .overlay { position: absolute; inset: 0; background: linear-gradient(to bottom, hsl(0 0% 0% / .6), hsl(0 0% 0% / .4), var(--background)); }
.about-hero .caption { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 0 1.5rem; }
.about-hero .caption .loc { font-size: .75rem; text-transform: uppercase; letter-spacing: .3em; color: var(--primary); font-weight: 500; margin-bottom: .75rem; }
.about-hero .caption h1 { font-family: var(--font-display); font-weight: 700; color: #fff; font-size: clamp(3rem, 7vw, 3.75rem); }
.story { padding: 4rem 1.5rem; max-width: 48rem; margin: 0 auto; text-align: center; }
.story h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.25rem, 5vw, 3rem); line-height: 1.15; margin-bottom: 1.5rem; }
.story p { color: var(--muted-foreground); font-size: 1.125rem; line-height: 1.7; }
.divider { max-width: 20rem; margin: 0 auto; border-top: 1px solid hsl(30 8% 18% / .4); }
.gallery-sec { padding-top: 3rem; padding-bottom: 4rem; }
.gallery { display: flex; gap: .75rem; overflow-x: auto; padding: 0 1rem 1rem; scroll-snap-type: x mandatory; }
.gallery::-webkit-scrollbar { display: none; }
.gallery { scrollbar-width: none; }
.gallery .shot { flex: none; width: 18rem; height: 16rem; border-radius: .75rem; overflow: hidden; background: var(--secondary); scroll-snap-align: start; }
.gallery .shot img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s ease; }
.gallery .shot img:hover { transform: scale(1.05); }
.eng-sec { padding: 3rem 1.5rem 6rem; max-width: 72rem; margin: 0 auto; }
.eng-grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; }
.eng-card { display: flex; flex-direction: column; background: var(--card); border: 1px solid hsl(30 8% 18% / .6); border-radius: 1rem; overflow: hidden; transition: border-color .3s; }
.eng-card:hover { border-color: hsl(38 90% 55% / .4); }
.eng-card .photo { aspect-ratio: 3/4; overflow: hidden; background: var(--secondary); }
.eng-card .photo img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s ease; }
.eng-card:hover .photo img { transform: scale(1.05); }
.eng-card .info { padding: 1.25rem; }
.eng-card .info h3 { font-size: 1.125rem; font-weight: 600; line-height: 1.2; }
.eng-card .info .role { font-size: .75rem; color: var(--primary); font-weight: 500; letter-spacing: .02em; margin: .15rem 0 .75rem; }

/* ---- Contact ---- */
.contact-wrap { max-width: 48rem; margin: 0 auto; padding: 4rem 1.5rem; }
.contact-head { text-align: center; margin-bottom: 3rem; }
.contact-head .eyebrow2 { color: var(--primary); font-weight: 600; text-transform: uppercase; letter-spacing: .15em; font-size: .875rem; }
.contact-head h1 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.25rem, 5vw, 3rem); margin: 1rem 0; }
.contact-head p { color: var(--muted-foreground); font-size: 1.125rem; }
.contact-grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; }
.contact-card { padding: 1.5rem; border-radius: 1rem; background: var(--card); border: 1px solid hsl(30 8% 18% / .5); transition: border-color .2s; }
a.contact-card:hover { border-color: hsl(38 90% 55% / .5); }
.contact-card .k { color: var(--primary); font-weight: 600; text-transform: uppercase; letter-spacing: .15em; font-size: .75rem; }
/* info@echonestrecording.com is a single 26-character token and at 320px it is wider than the card's
   content box on its own, which pushed the whole document sideways. Pre-existing, and previously
   hidden behind a larger header overflow at the same width.

   `anywhere`, not `break-word`: the card is a grid item, so it will not shrink below its min-content
   width, and only `anywhere` reduces the min-content contribution of an unbreakable word.
   `break-word` breaks the line visually but leaves min-content unchanged — measured, it did not fix
   this. */
.contact-card .v { font-weight: 600; font-size: 1.125rem; margin: .5rem 0 .25rem; overflow-wrap: anywhere; }
.contact-card .note { font-size: .875rem; color: var(--muted-foreground); }
.contact-cta { text-align: center; margin-top: 3rem; }

/* ---- Return Policy ---- */
.policy-wrap { max-width: 48rem; margin: 0 auto; padding: 2.5rem 1.5rem; }
.policy-wrap > .intro { margin-bottom: 2.5rem; }
/* h1 as well as h2 since 2026-08-06, when the page title was promoted to h1 — it was the site's
   only page with no h1 at all. Purely a semantics change; these two must keep rendering identically
   so the fix stays invisible. */
.policy-wrap .intro h1,
.policy-wrap .intro h2 { font-family: var(--font-display); font-size: 1.875rem; font-weight: 700; margin-bottom: .5rem; }
.policy-wrap .intro p { font-size: .875rem; color: var(--muted-foreground); }
.policy-section { margin-bottom: 2.5rem; }
.policy-section h3 { font-size: 1.125rem; font-weight: 600; color: var(--primary); border-bottom: 1px solid hsl(30 8% 18% / .4); padding-bottom: .5rem; margin-bottom: 1rem; }
.policy-item { margin-bottom: 1rem; }
.policy-item .h { font-weight: 600; margin-bottom: .25rem; }
.policy-item p { font-size: .875rem; color: var(--muted-foreground); line-height: 1.6; }
.policy-foot { padding-top: 1rem; border-top: 1px solid hsl(30 8% 18% / .4); font-size: .75rem; color: var(--muted-foreground); }
.back-link { display: inline-flex; align-items: center; gap: .5rem; font-size: .875rem; color: var(--muted-foreground); }
.back-link:hover { color: var(--foreground); }

/* ---- responsive ---- */
@media (min-width: 640px) {
  .feature-grid { grid-template-columns: repeat(3, 1fr); }
  .price-grid { grid-template-columns: repeat(2, 1fr); }
  .contact-grid { grid-template-columns: repeat(2, 1fr); }
  .eng-grid { grid-template-columns: repeat(2, 1fr); }
  footer.site .cols { flex-direction: row; justify-content: space-between; text-align: left; }
  footer.site img.logo { margin-left: 0; }
  .gallery .shot { width: 24rem; height: 20rem; }
}
@media (min-width: 1024px) {
  .eng-grid { grid-template-columns: repeat(4, 1fr); }
}

/* ==========================================================================
   WordPress-specific additions — not in the static preview, but required once
   content comes from the block editor and the admin bar is present.
   ========================================================================== */

/* Prose rendered via the_content() inherits the surrounding section's styling,
   but editor output needs its own link/list/emphasis rules. */
.entry-content a { color: var(--primary); text-decoration: underline; }
.entry-content a:hover { color: hsl(38 90% 65%); }
.entry-content p + p { margin-top: 1rem; }
.entry-content ul, .entry-content ol { margin: 1rem 0 1rem 1.5rem; }
.entry-content li { margin-bottom: .5rem; }
.entry-content strong { color: var(--foreground); font-weight: 600; }
.entry-content h2, .entry-content h3, .entry-content h4 { font-family: var(--font-display); margin: 1.5rem 0 .75rem; }

/* Standard WP media alignment classes. */
.alignleft { float: left; margin: 0 1.5rem 1rem 0; }
.alignright { float: right; margin: 0 0 1rem 1.5rem; }
.aligncenter { display: block; margin: 1rem auto; }
.alignwide, .alignfull { max-width: 100%; }
.wp-caption { max-width: 100%; }
.wp-caption-text { font-size: .75rem; color: var(--muted-foreground); text-align: center; margin-top: .5rem; }

/* Accessibility: visually hidden but available to screen readers, and a skip
   link that becomes visible on keyboard focus. */
.screen-reader-text {
  border: 0; clip-path: inset(50%); height: 1px; margin: -1px; overflow: hidden;
  padding: 0; position: absolute; width: 1px; word-wrap: normal !important;
}
.skip-link:focus {
  clip-path: none; background: var(--primary); color: var(--primary-foreground);
  height: auto; width: auto; padding: 1rem 1.5rem; left: 1rem; top: 1rem;
  z-index: 100000; font-weight: 600; border-radius: .5rem;
}

/* The fixed admin bar would otherwise cover the full-height hero. */
.admin-bar .hero { min-height: calc(100vh - 32px); }
@media screen and (max-width: 782px) {
  .admin-bar .hero { min-height: calc(100vh - 46px); }
}

/* 404 */
.notfound-wrap { max-width: 48rem; margin: 0 auto; padding: 6rem 1.5rem; text-align: center; }
.notfound-wrap h1 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.25rem, 5vw, 3rem); margin-bottom: 1rem; }
.notfound-wrap p { color: var(--muted-foreground); font-size: 1.125rem; margin-bottom: 2rem; }

/* wp_nav_menu emits <ul><li>, which the hand-written preview nav did not have. Flatten the list so
   the flex layout and spacing stay identical to the approved design, and honour WordPress's own
   current-menu-item class alongside the preview's .active. */
.topbar nav ul.menu { display: flex; align-items: center; gap: 1.5rem; list-style: none; margin: 0; padding: 0; }
.topbar nav ul.menu li { list-style: none; margin: 0; padding: 0; }
.topbar nav .current-menu-item > a,
.topbar nav .current_page_item > a { color: var(--primary); font-weight: 500; }
footer.site .links ul.menu { display: flex; align-items: center; gap: 1.5rem; list-style: none; margin: 0; padding: 0; }
footer.site .links ul.menu li { list-style: none; margin: 0; padding: 0; }

/* Small screens: let a longer menu wrap rather than overflow the bar. */
@media (max-width: 520px) {
  .topbar .inner { flex-wrap: wrap; justify-content: center; row-gap: .75rem; }
  .topbar nav ul.menu { flex-wrap: wrap; justify-content: center; gap: 1rem; }
  footer.site .links ul.menu { flex-wrap: wrap; justify-content: center; }
}


/* ==========================================================================
   v2 — the reimagined site.
   Same tokens, same identity. What is new is structure: a real header on every
   page, and the sections the old site had no room for (gear, amenities, the
   neighborhood, a gallery, engineer credits, and the members/store shells).
   ========================================================================== */

/* ---- site header ------------------------------------------------------- */

/* Solid and sticky everywhere; transparent and fixed over the home hero so the
   photograph runs edge to edge under it, filling in once you scroll past. */
.sitebar {
  position: sticky; top: 0; z-index: 50;
  padding: .85rem 1.5rem;
  background: hsl(30 5% 6% / .92);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid hsl(30 8% 18% / .6);
  transition: background .3s ease, border-color .3s ease, padding .3s ease;
}
.sitebar.over-hero {
  position: fixed; left: 0; right: 0;
  background: transparent; border-bottom-color: transparent; backdrop-filter: none;
  padding: 1.4rem 1.5rem;
}
.sitebar.over-hero.scrolled {
  background: hsl(30 5% 6% / .92); border-bottom-color: hsl(30 8% 18% / .6);
  backdrop-filter: blur(12px); padding: .85rem 1.5rem;
}
.sitebar .inner { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; }
.sitebar .brand { flex: none; }
.sitebar .brand-mark {
  font-family: var(--font-brand); font-size: 1.45rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; white-space: nowrap; line-height: 1;
}
.sitebar .brand:hover .brand-mark { color: var(--primary); }
.sitebar .bar-actions { display: flex; align-items: center; gap: .75rem; flex: none; }
.btn-sm { padding: .6rem 1.25rem; font-size: .875rem; }

/* ---- the partner mirror's four chrome states -----------------------------

   echonest_preview_chrome_bar() and echonest_preview_chrome_drawer() (functions.php) render 'out',
   'free', 'member' and 'engineer' all at once on every crawled page; a script in <head> (header.php)
   reads the member app's own cookie before first paint and stamps html[data-chrome]. Everything
   below is what turns that stamp into "one of these four is visible" — round 7 (2026-08-28).

   ⚠️ display: contents, NOT display: block, on the winner. These wrappers exist only to be
   individually hidden; the elements inside them (.bar-benefits, .acct-wrap, the plain Sign In link)
   are what .bar-actions and .drawer-nav actually lay out as flex items, exactly as they were laid
   out before this wrapper existed. Reveal the wrapper as a block and it becomes an extra flex item
   with the wrong gap on either side of it.

   ⚠️ NEVER MATCHES ON LIVE OR ORDINARY STAGING. [data-chrome-state] is emitted only from the two
   functions above, which return immediately unless ECHONEST_PREVIEW_ALL_STATES is defined — nothing
   here has an effect anywhere that flag is not set. */
[data-chrome-state] { display: none; }
html[data-chrome="out"] [data-chrome-state="out"],
html[data-chrome="free"] [data-chrome-state="free"],
html[data-chrome="member"] [data-chrome-state="member"],
html[data-chrome="engineer"] [data-chrome-state="engineer"],
/* No script ran — JavaScript off, or the attribute has not landed yet. Same default the script
   itself falls back to: signed out, the "original site". */
html:not([data-chrome]) [data-chrome-state="out"] { display: contents; }

/* ---- account control (top right) --------------------------------------- */

/* The corner every member site puts the account, notifications and messages cluster in. For now it
   is one glyph and one destination.

   Muted, not amber, and no badge. Two amber controls side by side would split the funnel — Book Now
   is the thing in this bar that should pull the eye — and an unread dot would be inventing a
   notification that nothing on the site can produce yet. The bell/messages/avatar cluster is drawn
   on /membership/ instead, inside the block that is labelled "Example". */
.account {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  height: 2.25rem; padding: 0 .9rem; border-radius: 999px;
  color: var(--muted-foreground);
  border: 1px solid hsl(30 8% 20% / .8);
  /* Same face as the nav links, which is Inter since 2026-08-16 — see the block above .nav-links a
     for why the brand face left this bar. The pill is the one element here with a border, so it
     keeps a lighter weight than the links: the shape is already doing the work weight would. */
  font-family: var(--font-sans);
  font-size: .875rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  white-space: nowrap;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.account svg { width: 1.05rem; height: 1.05rem; flex: none; }
.account:hover, .account:focus-visible { color: var(--foreground); border-color: hsl(30 8% 28% / .9); background: hsl(30 5% 12% / .6); }
.account.active { color: var(--primary); border-color: hsl(38 90% 55% / .35); }

/* Below the nav breakpoint the bar is wordmark + account + Book + hamburger, and the word will not
   fit. It collapses to the glyph — but the label stays in the DOM as screen-reader text rather than
   being display:none'd, so the link keeps the accessible name "Sign In" at every width. */
@media (max-width: 900px) {
  .account { width: 2.25rem; padding: 0; gap: 0; border-radius: 50%; }
  .account .acct-label {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* "Member Benefits" — the sell link, immediately left of Sign In. Plain text with no border, so the
   pair reads as one account corner with a single pill in it rather than as two competing buttons,
   and no icon, because the bar is allowed exactly one SVG and the harness counts it.

   Muted like .account and never amber: Book Now is the only thing in this bar that should pull the
   eye. */
/* Same face as the nav links it sits beside — Inter, since 2026-08-16. Kept a notch smaller and
   lighter than the nav proper: it is the secondary item in the corner, and the hierarchy has to
   survive the shared typeface.

   ⚠️ IT IS NO LONGER --muted-foreground. Randy named this link first when he said the header was
   *"a little too dark"*, and at 50% lightness it was the dimmest text in the bar by a wide margin.
   --secondary-foreground puts it one clear step under the links instead of two. */
.bar-benefits {
  font-family: var(--font-sans);
  color: var(--secondary-foreground);
  font-size: .875rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  white-space: nowrap;
  transition: color .2s ease;
}
.bar-benefits:hover, .bar-benefits:focus-visible { color: var(--foreground); }
.bar-benefits.active { color: var(--primary); }

/* Dropped below 1024px, where the bar is out of room — at 900px the Sign In label has already
   collapsed to a glyph and by 380px the account control goes entirely. Unlike .acct-label this is
   display:none rather than screen-reader text: it is a whole link with its own destination, and
   the drawer carries that destination in words at every width, so hiding it here loses nothing.
   Leaving an invisible link in the tab order at mobile widths would be the worse outcome. */
@media (max-width: 1023px) {
  .bar-benefits { display: none; }
}

/* ---- signed-in account menu (STAGING ONLY) ----------------------------- */

/* Everything below draws markup that only exists under echonest_demo_state() — false whenever
   WP_DEBUG is off, so none of it can render on the live site. It is the signed-in view of the member
   product, built so the partners can see the whole feature set and where the free/paid line falls
   without any of it being built. There is no auth behind it; "signed in" is a cookie.

   The corner keeps .account's geometry and muted palette. Book Now is still the only thing in this
   bar allowed to pull the eye, and a signed-in member has less reason to be pulled anywhere, not
   more. */
.acct-wrap { position: relative; flex: none; }

/* THE SIGNED-IN TRIGGER IS A <button> AND HAS TO BE UNDRESSED BY HAND. The reset at the top of this
   file is only `* { margin: 0; padding: 0; box-sizing: border-box }` — it never touches the UA button
   appearance, so this rendered with `background-color: buttonface`: a light grey pill in a bar where
   every other control is a muted outline. That is the "white icon" Randy flagged, and it was a bug
   rather than a styling choice.

   Same treatment .menu-toggle already gives itself, plus `font: inherit`, which buttons need because
   they do not inherit type. The result is visually identical to the signed-out .account link. */
.acct-signed {
  appearance: none; -webkit-appearance: none;
  background: transparent; font: inherit; cursor: pointer;
  gap: .5rem; padding: 0 .7rem;
}
.acct-signed .acct-msg, .acct-signed .acct-avatar { display: inline-flex; position: relative; }

/* The unread dot. Allowed here and nowhere else in global chrome: the signed-out bar must never
   carry one, because nothing on the live site can produce a notification. Inside an opt-in demo that
   captions itself, it reads as a picture of the feature. */
.acct-msg.has-dot::after {
  content: ""; position: absolute; top: -.1rem; right: -.15rem;
  width: .4rem; height: .4rem; border-radius: 50%;
  background: var(--primary);
}

/* The panel. Right-aligned to the corner it drops from, and capped against the viewport so it can
   never grow the initial containing block — a wide element in the bar drags the fixed float bar out
   with it even when the page itself cannot scroll. `contain: paint` is the belt to that braces. */
.acct-menu {
  position: absolute; top: calc(100% + .6rem); right: 0; z-index: 60;
  width: 19rem; max-width: calc(100vw - 2rem);
  padding: .4rem;
  background: hsl(30 6% 8% / .98);
  border: 1px solid hsl(30 8% 20% / .9); border-radius: .9rem;
  box-shadow: 0 18px 40px hsl(0 0% 0% / .5);
  contain: paint;
}
.acct-menu[hidden] { display: none; }

/* The head is what keeps the menu honest — it names the plan being previewed and says in one line
   that no real account is behind it. If the caption ever goes, the menu starts claiming accounts
   work. */
.am-head { padding: .6rem .75rem .5rem; }
.am-plan {
  display: block; font-family: var(--font-brand); font-size: 1rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--foreground);
}
.am-demo { display: block; margin-top: .1rem; font-size: .6875rem; color: var(--muted-foreground); }

.am-sep { height: 1px; margin: .35rem .35rem; background: hsl(30 8% 18% / .9); }

/* Group headings, in the site's own .eyebrow idiom (small amber caps, .25em tracking) at menu scale.
   They replaced bare hairline rules: fourteen undifferentiated rows read as a browser widget, four
   labelled groups read as a product. */
.am-group {
  padding: .85rem .75rem .3rem;
  font-size: .5625rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .25em;
  color: var(--primary); opacity: .8;
}
.am-group:first-of-type { padding-top: .3rem; }

/* One row geometry for links and inert rows alike. The inert ones are NOT greyed and NOT disabled:
   the feature is unwritten, not broken, and a menu full of dead-looking controls tells a partner the
   wrong story. They simply do not respond to a cursor.

   SET IN INTER, NOT BIG SHOULDERS. The brand face was tried here for one round and Randy cut it:
   *"its a cool font you picked but the narrow quality makes it hard to read."* Big Shoulders is a
   condensed display face — it earns its keep in the wordmark and in the one-line panel head, and it
   loses legibility fast at list scale where thirteen labels have to be scanned rather than read.
   The menu gets its character from the amber group headings and the violet upgrade path instead. */
.am-row {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: center; gap: .35rem;
  padding: .48rem .75rem; border-radius: .55rem;
  font-size: .875rem; font-weight: 450;
  color: var(--foreground);
  transition: color .18s ease, background .18s ease;
}
.am-label { flex: 1 1 auto; min-width: 0; }

/* Hover is an amber rule growing from the left edge rather than a flat grey fill — the same gesture
   the nav uses, and it leaves the row's own colour doing the work. scaleY keeps it off the paint
   path; a width/height transition here would relayout the row. */
a.am-row::before {
  content: ""; position: absolute; left: .1rem; top: .35rem; bottom: .35rem; width: 2px;
  background: var(--primary); border-radius: 2px;
  transform: scaleY(0); transform-origin: 50% 50%;
  transition: transform .18s ease;
}
a.am-row:hover, a.am-row:focus-visible { color: var(--primary); background: hsl(30 5% 13% / .7); }
a.am-row:hover::before, a.am-row:focus-visible::before { transform: scaleY(1); }
.am-inert { cursor: default; }

/* THE ONE MOMENT IN THE PANEL. Violet, a faint wash and a soft glow — the "something exciting
   happened" Randy asked for, spent on the single row where an exciting thing could actually happen.

   It is not a .btn and it is not amber, both deliberately: a filled amber button here would sit a
   couple of inches under the amber Book Now in the bar and split the eye between two CTAs. In violet
   it reads as a different kind of action, which is what it is — subscribing, not booking a room. */
.am-upgrade {
  margin: .3rem .1rem .15rem; padding: .6rem .75rem;
  justify-content: space-between;
  border: 1px solid hsl(288 60% 66% / .4); border-radius: .6rem;
  background: linear-gradient(100deg, hsl(288 60% 66% / .16), hsl(288 60% 66% / .05));
  color: var(--accent); font-weight: 600;
  box-shadow: 0 0 0 0 hsl(288 60% 66% / 0);
  transition: border-color .18s ease, box-shadow .25s ease, background .18s ease;
}
.am-upgrade::after {
  content: "→"; font-size: .95em; opacity: .8;
  transition: transform .18s ease;
}
.am-upgrade:hover, .am-upgrade:focus-visible {
  background: linear-gradient(100deg, hsl(288 60% 66% / .26), hsl(288 60% 66% / .1));
  border-color: hsl(288 60% 66% / .7);
  box-shadow: 0 0 18px -4px hsl(288 60% 66% / .5);
  color: var(--accent);
}
.am-upgrade:hover::after, .am-upgrade:focus-visible::after { transform: translateX(3px); }
.am-upgrade::before { content: none; }

/* The paid group's heading takes the accent too, so the violet reads as one idea running down the
   panel — heading, pills, upgrade row — rather than three unrelated coloured things. */
.am-group-paid { color: var(--accent); }

/* The pill marking a gated feature. It keeps .rental-badge's geometry — same padding, radius, size,
   tracking — so the two read as siblings, but it is VIOLET, not amber.

   That is the one distinction the whole palette change exists to draw: amber is booking (and Book
   Now is the site's only amber CTA), violet is membership. The amber "Member rental" pills on
   /studio/ and /membership/ are untouched and stay amber — they mark a fact about a piece of gear,
   which is a different claim from "upgrade to get this".

   Scoped to .acct-menu deliberately: .rental-badge is defined LATER in this file, so a bare .am-pill
   ties on specificity and loses on source order — it rendered amber with every rule apparently
   correct. Two classes beat one, and it also guarantees this recolour can never reach the gear
   list. */
.acct-menu .am-pill {
  margin-left: 0; flex: none;
  background: hsl(288 60% 66% / .14); border-color: hsl(288 60% 66% / .45); color: var(--accent);
}

/* Only two rows carry a note, and both are rows where the free/paid line runs through the row rather
   than beside it. Full width so it sits under the label rather than fighting the pill for space.

   Back to Inter, explicitly: the row above it is set in the condensed display face, but a note is a
   sentence and that face is for labels. Without this it would inherit Big Shoulders from .am-row. */
.am-note {
  flex: 0 0 100%;
  font-family: var(--font-sans); font-size: .6875rem; font-weight: 400;
  letter-spacing: 0; line-height: 1.35;
  color: var(--muted-foreground);
}

/* On a free account the paid rows stay listed and stay readable — they are what upgrading buys, so
   hiding them would empty the menu of its argument. Dimmed just enough to read as "not yours yet". */
.am-locked .am-label { color: var(--muted-foreground); }

.am-signout { color: var(--muted-foreground); }

/* In the drawer the menu is the content, not a dropdown: no absolute position, no panel chrome, and
   full width. Below 1024px the corner has nowhere to hang a panel from, so this is the only way the
   member product is reachable on a phone. */
.drawer-member { margin-top: .75rem; width: 100%; }
.drawer-member .acct-menu {
  position: static; width: auto; max-width: none;
  padding: 0; background: none; border: 0; border-radius: 0; box-shadow: none;
}
.drawer-member .am-head { padding-left: 0; padding-right: 0; }
/* `.drawer-nav a` sets the display face at 1.75rem for the nav links, and it beats a bare .am-row on
   specificity — so a menu row that happens to be a LINK rendered three times the size of the inert
   rows beside it, in a different typeface. The rows have to look identical whether or not the feature
   behind them is built yet, so this restates the row's own type at a weight that wins. */
.drawer-nav .drawer-member .am-row {
  font-family: var(--font-sans); font-size: 1rem; font-weight: 450; letter-spacing: 0;
  color: var(--foreground);
  padding-left: 0; padding-right: 0;
}
.drawer-nav .drawer-member .am-locked .am-label,
.drawer-nav .drawer-member .am-signout { color: var(--muted-foreground); }
.drawer-nav .drawer-member a.am-row:hover { color: var(--primary); background: none; }
/* The left-edge rule has nowhere to sit once the row loses its horizontal padding. */
.drawer-nav .drawer-member a.am-row::before { content: none; }
.drawer-nav .drawer-member .am-upgrade {
  margin: .5rem 0; padding: .7rem .85rem; color: var(--accent);
}
.drawer-nav .drawer-member a.am-upgrade:hover { color: var(--accent); }
.drawer-member .am-sep { margin-left: 0; margin-right: 0; }

/* At the narrowest widths the corner is a bare glyph pair, so the panel would otherwise hang off the
   right edge of a 320px screen. It is pinned to the gutter instead. */
@media (max-width: 480px) {
  .acct-menu { right: -.5rem; width: auto; min-width: 15rem; }
  .drawer-member .acct-menu { right: auto; min-width: 0; }
}

/* ── THE BAR IS SET IN THE TEXT FACE, TRACKED CAPS ───────────────────────────────────────────────

   ⚠️ THE BRAND FACE WAS TRIED HERE AND RANDY CUT IT — 2026-08-16, the second time he has rejected
   Big Shoulders for reading rather than for logos: *"the text in the header like 'member benefits'
   and 'studio' is a little too dark and thin. I don't like the font, it feels a little Halloween."*
   The first time was the account menu (see .am-row below) — *"its a cool font you picked but the
   narrow quality makes it hard to read."* Two rejections on the same grounds is a rule now:

     ⚠️ BIG SHOULDERS IS FOR THE WORDMARK AND THE FOOTER, NEVER FOR SOMETHING BEING READ. It is a
     condensed display face. Tall narrow capitals in a row, tracked out and dim, is the shape he
     calls Halloween, and no amount of weight fixes the letterforms.

   So: Inter, the face the whole site already reads in, at 700 and at --foreground. Three things had
   to move together and all three were part of the complaint:

     · THE FACE, because condensed was the objection.
     · THE WEIGHT, 600 -> 700. Inter caps at 600 look lighter than Big Shoulders caps at 600 — the
       condensed face packs more ink into the same width.
     · THE COLOUR, but only one step and NOT to --foreground. It went to 95% for one round and Randy
       sent it back the same day: *"font slightly darker on header items (keep the echonest logo the
       same). the new font selection is good but it matches the echonest on the far left so much that
       doesn't look as much like a clickable menu."*

   ⚠️ --secondary-foreground IS A FLOOR AND A CEILING HERE, and the ceiling is the reason. .brand-mark
   sets no colour at all, so the wordmark inherits --foreground from body — put the links there and
   the bar becomes one undifferentiated row of 95% text, and a menu that matches the logo stops
   reading as a menu. The links have to sit one clear step under it. The extra weight is what stops
   that step reading as "thin", which is the half of the original complaint this does not undo.

   ⚠️ THE SIZE GOES BACK DOWN, 1.0625rem -> .9375rem. It was raised only because a condensed face at
   the same nominal size reads noticeably smaller; Inter does not, and left at 17px it crowds the
   1024-1200px band where three links, Member Benefits, Sign In and Book Now all have to fit.

   ⚠️ THE UNDERLINE STAYS. It is what makes this a designed nav rather than a font choice, and it is
   not what he objected to. Do not "simplify" it away while tidying this rule.

   ⚠️ AND NOT Playfair, which is what the drawer uses. A serif at 15px, uppercase, in a horizontal
   row is fussy at exactly the size where legibility matters most. The drawer carries it because
   there it is 28px and stacked. */
.nav-links { display: flex; align-items: center; gap: 1.75rem; }
.nav-links ul.menu { display: flex; align-items: center; gap: 1.75rem; list-style: none; margin: 0; padding: 0; }
.nav-links li { list-style: none; margin: 0; padding: 0; }
.nav-links a {
  position: relative;
  font-family: var(--font-sans);
  font-size: .9375rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--secondary-foreground);
  transition: color .2s ease; white-space: nowrap;
}

/* The underline. It is what makes this a designed nav rather than a font swap: a hairline in the
   amber accent that grows from the left on hover and sits under whichever item you are on.

   Drawn on a pseudo-element rather than with text-decoration or a border so that (a) it can animate
   width without shifting the row, and (b) it clears the uppercase baseline properly — an underline
   on tracked capitals sits too close by default and reads as a strikethrough on the descender-less
   letterforms this face is full of.

   scaleX from the left, not width, so it is a compositor-only animation. */
.nav-links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -.45rem; height: 1px;
  background: var(--primary);
  transform: scaleX(0); transform-origin: left center;
  transition: transform .25s ease;
}
.nav-links a:hover { color: var(--primary); }
.nav-links a:hover::after { transform: scaleX(1); }

.nav-links a.active,
.nav-links .current-menu-item > a,
.nav-links .current_page_item > a { color: var(--primary); font-weight: 700; }
.nav-links a.active::after,
.nav-links .current-menu-item > a::after,
.nav-links .current_page_item > a::after { transform: scaleX(1); }

/* Nobody asked for motion. Under prefers-reduced-motion the underline still appears, it just
   appears — which is the state that carries the information. */
@media (prefers-reduced-motion: reduce) {
  .nav-links a::after { transition: none; }
}

/* Hamburger. Hidden until the links no longer fit. */
.menu-toggle {
  display: none; background: transparent; border: 1px solid var(--border);
  border-radius: .6rem; padding: .5rem .6rem; cursor: pointer; color: inherit;
}
.menu-toggle .bars { display: block; width: 1.15rem; }
.menu-toggle .bars span {
  display: block; height: 2px; background: currentColor; border-radius: 2px;
  transition: transform .25s ease, opacity .25s ease;
}
.menu-toggle .bars span + span { margin-top: 4px; }
.menu-toggle[aria-expanded="true"] .bars span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.menu-toggle[aria-expanded="true"] .bars span:nth-child(2) { opacity: 0; }
.menu-toggle[aria-expanded="true"] .bars span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* ---- mobile drawer ----------------------------------------------------- */

.drawer {
  position: fixed; inset: 0; z-index: 60;
  background: hsl(30 5% 6% / .98); backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
}
.drawer[hidden] { display: none; }
.drawer-nav { display: flex; flex-direction: column; align-items: center; gap: 1.5rem; padding: 2rem; }
.drawer-nav a { font-family: var(--font-display); font-size: 1.75rem; color: var(--foreground); }
.drawer-nav a:hover, .drawer-nav a.active { color: var(--primary); }
.drawer-nav .drawer-book { font-family: var(--font-sans); font-size: 1rem; margin-top: 1rem; }
body.drawer-open { overflow: hidden; }

/* The drawer carries more than the three-item bar does. The pages that left the bar live below this
   divider — quieter and smaller, but present, because a phone is where navigation is hardest and a
   three-link drawer would have been the regression. */
.drawer-sep {
  display: flex; align-items: center; gap: .75rem;
  width: 100%; max-width: 16rem; margin: .25rem 0 -.25rem;
  font-size: .6875rem; text-transform: uppercase; letter-spacing: .18em;
  color: var(--muted-foreground);
}
.drawer-sep::before, .drawer-sep::after { content: ""; flex: 1; height: 1px; background: hsl(30 8% 18% / .8); }
.drawer-nav a.drawer-sub { font-family: var(--font-sans); font-size: 1.0625rem; color: var(--muted-foreground); }
.drawer-nav a.drawer-sub:hover, .drawer-nav a.drawer-sub.active { color: var(--primary); }
.drawer-nav a.drawer-account { display: inline-flex; align-items: center; gap: .5rem; }
.drawer-nav a.drawer-account svg { width: 1.05rem; height: 1.05rem; }

/* ---- shared section furniture ------------------------------------------ */

/* Alternating bands give the long pages rhythm without adding another colour. */
.band { background: hsl(30 5% 8% / .55); border-top: 1px solid hsl(30 8% 18% / .35); border-bottom: 1px solid hsl(30 8% 18% / .35); }

/* A band with a photograph behind it. Used once, on the package menu on /services/, where two grey
   bands ran into each other and the prices read flat ("very lackluster" — Randy, 2026-08-03).

   The photo is a backdrop and nothing else: heavily dimmed, under a scrim, with the section's own
   translucent band colour still on top of it. If the type ever gets hard to read, lower .band-photo's
   opacity — the fix is never brighter text or a thinner scrim. overflow:hidden keeps the layers off
   the page's width; the overflow sweep in the harness proves it at 320. */
.band-shot { position: relative; overflow: hidden; }
.band-photo {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 60%; opacity: .2;
}
.band-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, hsl(30 5% 6% / .8), hsl(30 5% 6% / .5), hsl(30 5% 6% / .85));
}
/* Everything that is not a layer sits above both — written against the wrapper rather than a named
   .wrap-* so the treatment drops onto any section (it is on the package menu, the amenities list and,
   in card form, the membership promo). */
.band-shot > *:not(.band-photo):not(.band-scrim) { position: relative; }

.sec-head { max-width: 44rem; margin: 0 auto 3.5rem; }
.sec-head .eyebrow { margin-bottom: .75rem; }
.sec-head h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2rem, 4.5vw, 2.75rem); margin-bottom: 1rem; }
.sec-head .sub { color: var(--muted-foreground); font-size: 1.0625rem; }

/* Placeholder marker. Rendered only in staging — see echonest_show_tbd(). */
.tbd-badge {
  display: inline-block; margin-left: .5rem; padding: .1rem .5rem; border-radius: 9999px;
  background: hsl(0 70% 55% / .15); border: 1px solid hsl(0 70% 55% / .4); color: hsl(0 80% 72%);
  font-family: var(--font-sans); font-size: .625rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; vertical-align: middle;
}
.is-tbd { opacity: .75; }

/* Member rental marker. Same pill geometry as .tbd-badge but AMBER, not red, and — unlike that one —
   it renders in production: this is a fact about the gear, not a note about missing copy. Applied to
   the rentable instruments on the gear list and repeated on the comparison table's rentals row, which
   is where the terms live — so this pill never carries a fee. */
.rental-badge {
  display: inline-block; margin-left: .5rem; padding: .1rem .5rem; border-radius: 9999px;
  background: hsl(38 90% 55% / .12); border: 1px solid hsl(38 90% 55% / .4); color: var(--primary);
  font-family: var(--font-sans); font-size: .625rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; vertical-align: middle; white-space: nowrap;
}
/* .gear-rental-note is gone with the line it styled — the gear list no longer explains the "Member
   rental" pills or links to /membership/ (Randy, 2026-08-03). The .rental-badge rules above stay:
   the pills themselves are still on the items, here and in the comparison table. */

.tag {
  display: inline-block; padding: .25rem .75rem; border-radius: 9999px;
  background: hsl(38 90% 55% / .12); border: 1px solid hsl(38 90% 55% / .35);
  color: var(--primary); font-size: .6875rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em;
}

/* A second pill next to the amber one, for a fact rather than a label — "Free to join" beside
   "Membership". Neutral, so the pair reads as one statement instead of two badges competing. */
.tag-quiet { background: hsl(30 8% 18% / .5); border-color: hsl(30 8% 24% / .8); color: var(--muted-foreground); }

/* ---- home: hero signal strip ------------------------------------------- */

/* Replaces the old "Open 24/7" pill. Concrete facts, not a slogan. */
/* The hero lead sits over a busy photograph — muted grey lost the contrast fight. */
.hero p.lead { color: var(--secondary-foreground); }

/* The 2026 shoot is genuinely dark — mean luminance around 30/255, against roughly
   twice that for the older set. v1's overlay was tuned for the bright teal photos
   and crushed these to near-black, losing the neon the room is actually known for.
   Lighter scrim, plus a small lift on the image itself. The bottom of the gradient
   still lands on the page background so the next section blends in. */
.hero .bg img { filter: brightness(1.18) saturate(1.06); }

/* Two layers doing different jobs. The radial darkens only the centre, where the
   headline and lead sit — the hero photo has a lit screen right behind that text.
   The linear keeps the top readable under the header and fades the bottom into the
   page. Scrimming the whole frame instead would flatten the neon and string lights
   at the edges, which are the most striking thing in the shot. */
.hero .bg::after {
  background:
    radial-gradient(ellipse 58% 50% at 50% 46%, hsl(30 5% 6% / .78), hsl(30 5% 6% / 0) 72%),
    linear-gradient(to bottom, hsl(30 5% 6% / .7), hsl(30 5% 6% / .34), hsl(30 5% 6% / .96));
}
.hero h1, .hero p.lead { text-shadow: 0 2px 14px hsl(30 5% 4% / .55); }
.page-hero img { filter: brightness(1.12) saturate(1.04); }

/* Some photographs have their own subject dead centre — the ON AIR sign, a logo on a
   screen — which is exactly where the page title lands. Shifting the crop drops the
   subject below the caption instead of swapping out the better photograph. */
.page-hero.focus-top img { object-position: center 22%; }
.page-hero .overlay {
  background: linear-gradient(to bottom, hsl(30 5% 6% / .45), hsl(30 5% 6% / .4), var(--background));
}

.signal-strip {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .5rem 1.5rem; list-style: none; margin: 3rem auto 0; padding: 0; max-width: 52rem;
}
.signal-strip li {
  font-size: .8125rem; font-weight: 500; letter-spacing: .04em;
  color: var(--secondary-foreground); text-transform: uppercase; position: relative;
  /* An item never breaks mid-phrase — "Koreatown, LA" stays one unit. */
  white-space: nowrap;
}
/* The amber pip sits in the gap AFTER each item except the last. It was a ::before in the gap
   BEFORE each item, which left an orphan pip hanging in the left margin every time the row wrapped
   — round 8 (2026-09-02), the partners' "looks off-centre" note. As a trailing mark, a wrapped
   line simply ends with a pip, which reads as "continues". Centred in the 1.5rem gap: half is
   .75rem, and the 3px dot is placed by its edge, so back it off 1.5px. */
.signal-strip li:not(:last-child)::after {
  content: ""; position: absolute; right: calc(-.75rem - 1.5px); top: 50%;
  width: 3px; height: 3px; border-radius: 50%; background: var(--primary); transform: translateY(-50%);
}

/* ---- icons -------------------------------------------------------------- */

/* Every icon on the site is one of the stroked SVGs in inc/icons.php, tinted amber
   by currentColor. v1 used system emoji, which rendered full-colour and read as
   stock template art against this palette. */
.ico svg { width: 1.35rem; height: 1.35rem; }
.info-card .ico, .amenity .ico, .service-card .ico, .eng-addon .ico { color: var(--primary); }

/* ---- info cards -------------------------------------------------------- */

.card-grid { display: grid; grid-template-columns: 1fr; gap: 1.25rem; }
.info-card {
  padding: 1.75rem; border-radius: 1rem; background: var(--card);
  border: 1px solid hsl(30 8% 18% / .6); transition: border-color .25s, transform .25s;
}
.info-card:hover { border-color: hsl(38 90% 55% / .35); transform: translateY(-2px); }
.info-card .ico {
  width: 2.75rem; height: 2.75rem; border-radius: .75rem; background: hsl(38 90% 55% / .1);
  display: flex; align-items: center; justify-content: center; font-size: 1.25rem; margin-bottom: 1rem;
}
.info-card h3 { font-size: 1.0625rem; font-weight: 600; margin-bottom: .5rem; }
.info-card p { font-size: .9375rem; color: var(--muted-foreground); line-height: 1.65; }

/* ---- split (copy beside media) ----------------------------------------- */

.split { display: grid; grid-template-columns: 1fr; gap: 3rem; align-items: center; }
.split-copy .eyebrow { margin-bottom: .75rem; }
.split-copy h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2rem, 4.5vw, 2.75rem); margin-bottom: 1.25rem; }
.split-copy p { color: var(--muted-foreground); font-size: 1.0625rem; line-height: 1.7; margin-bottom: 1.75rem; }
.split-media img { width: 100%; border-radius: 1rem; border: 1px solid hsl(30 8% 18% / .6); }

/* ---- signal chain, over the rack --------------------------------------- */

/* Replaces .chain — four equally-sized bordered boxes with numbered circles, which is the most
   generic shape available and was carrying the most specific thing in the building. It also ran
   twice, byte for byte, on home and /studio.

   Mobile first here is not a formality: the photograph stacks ABOVE the steps below 900px and the
   text never sits on it. The shoot is dark (mean luminance ~27/255 on this frame) and small pale
   type over it at 375px is unreadable no matter how heavy the scrim. */
.chain-feature {
  position: relative; border-radius: 1.25rem; overflow: hidden;
  border: 1px solid hsl(30 8% 18% / .6); background: var(--card);
}
/* Left at 16:9. A 4:3 crop was tried when Randy said the block needed to be larger and reverted the
   same day — *"I wasn't talking about cropping the image on The Chain. I mean the entire design isn't
   large enough as a whole."* The block grew instead: taller at desktop, bigger type throughout. */
.cf-media { position: relative; aspect-ratio: 16 / 9; }
/* object-position favours the lower right: that is where the 6176 sits in the rack and where the
   HS8s are. A centred crop of this frame cuts both off and leaves an empty wall. */
.cf-media img { width: 100%; height: 100%; object-fit: cover; object-position: 62% 68%; display: block; }
/* Bottom-weighted, and deliberately light up top — the neon and the booth window are the reason to
   use this photograph at all, and a flat overlay heavy enough for body text erases them. */
.cf-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, hsl(30 5% 6% / .1) 0%, hsl(30 5% 6% / .35) 50%, hsl(30 5% 6% / .88) 100%);
}
/* THE WHOLE BLOCK IS SCALED UP, on Randy's note of 2026-08-03: *"the entire design isn't large enough
   as a whole."* Not a crop change and not one number — the heading, the lead, the step rows and the
   block's height all moved together, because a bigger photograph under the same small type just reads
   as more empty room. If it needs to grow again, move all of them again. */
.cf-body { padding: 2.25rem 2rem 2.5rem; }
.cf-body .eyebrow { margin-bottom: .85rem; }
.cf-body h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.2rem, 5.2vw, 3.5rem); margin-bottom: 1.15rem; }
.cf-lead { color: var(--muted-foreground); font-size: 1.15rem; line-height: 1.7; margin-bottom: 2.25rem; max-width: 38rem; }

/* The steps: a flow, not a row of cards. No borders, no fills — the rule and the numeral carry
   the ordering, which is all the boxes were ever doing. */
.cf-steps {
  list-style: none; margin: 0 0 2rem; padding: 0;
  display: grid; gap: 0; counter-reset: cf;
}
.cf-steps li {
  position: relative; counter-increment: cf;
  padding: 1.15rem 0 1.15rem 3rem;
  border-top: 1px solid hsl(30 8% 18% / .5);
}
.cf-steps li:last-child { border-bottom: 1px solid hsl(30 8% 18% / .5); }
.cf-steps li::before {
  content: counter(cf); position: absolute; left: 0; top: 1.15rem;
  width: 1.7rem; height: 1.7rem; border-radius: 50%;
  border: 1px solid hsl(38 90% 55% / .35); color: var(--primary);
  font-size: .75rem; font-weight: 700; font-variant-numeric: tabular-nums;
  display: flex; align-items: center; justify-content: center;
}
.cf-step { display: block; font-size: .75rem; text-transform: uppercase; letter-spacing: .18em; color: var(--muted-foreground); }
.cf-what { display: block; font-weight: 600; font-size: 1.25rem; margin-top: .25rem; }
.cf-or { color: var(--muted-foreground); font-weight: 400; font-size: 1rem; padding: 0 .15rem; }
.cf-mic { white-space: nowrap; }
.cf-note { display: block; font-size: .875rem; color: var(--muted-foreground); margin-top: .25rem; }
/* The mic step reads like the other three. It carried an amber .cf-what until round 8 (2026-09-02) —
   the partners found the orange text loud against the photograph. That the mic is the one step you
   choose is already carried by "Your call at booking" in the .cf-note. */

/* ---- home: gallery strip ----------------------------------------------- */

.strip { display: flex; gap: .75rem; overflow-x: auto; padding: 0 1.5rem 1rem; scroll-snap-type: x mandatory; scrollbar-width: none; }
.strip::-webkit-scrollbar { display: none; }
.strip .shot { flex: none; width: 17rem; height: 13rem; border-radius: .85rem; overflow: hidden; background: var(--secondary); scroll-snap-align: start; }
.strip .shot img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s ease; }
.strip .shot:hover img { transform: scale(1.06); }

/* ---- amenities + neighborhood ------------------------------------------ */

.amenity-grid { display: grid; grid-template-columns: 1fr; gap: 1.25rem; }
.amenity { display: flex; gap: 1rem; align-items: flex-start; padding: 1.25rem; border-radius: .85rem; background: hsl(30 5% 9% / .5); border: 1px solid hsl(30 8% 18% / .5); }
.amenity .ico {
  flex: none; width: 2.5rem; height: 2.5rem; border-radius: .7rem; background: hsl(38 90% 55% / .1);
  display: flex; align-items: center; justify-content: center; font-size: 1.15rem;
}
.amenity .label { font-weight: 600; font-size: .9375rem; }
.amenity .desc { font-size: .875rem; color: var(--muted-foreground); margin-top: .25rem; line-height: 1.6; }

.hood { margin-top: 3.5rem; padding: 2rem; border-radius: 1.25rem; border: 1px solid hsl(38 90% 55% / .25); background: hsl(38 90% 55% / .04); }
.hood h3 { font-family: var(--font-display); font-size: 1.5rem; font-weight: 700; margin-bottom: 1.75rem; text-align: center; }
.hood-grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; }
.hood-item .label { font-weight: 600; font-size: .9375rem; color: var(--primary); margin-bottom: .35rem; }
.hood-item .desc { font-size: .875rem; color: var(--muted-foreground); line-height: 1.6; }

/* ---- home: members promo ----------------------------------------------- */

/* DASHED IS THIS SITE'S PLACEHOLDER SHAPE, and .promo is where that convention is written down.
   Every current user overrides it to solid (.promo-members below), because everything on the site
   is now built — the last genuine placeholder, the beat store block on /membership/, was deleted on
   2026-08-07. Keep this default anyway: the next unbuilt thing should look unbuilt, and re-deriving
   that from scratch is how a stub ends up drawn as a finished feature. */
.promo { text-align: center; padding: 3rem 2rem; border-radius: 1.5rem; border: 1px dashed hsl(38 90% 55% / .35); background: hsl(38 90% 55% / .04); }
.promo .tag { margin-bottom: 1.25rem; }
.promo .tag + .tag { margin-left: .5rem; }

/* The membership block, textured (Randy, 2026-08-03: *"could we texturize the An account for
   everything you make here section better"*). It was a dashed outline round centred text — the shape
   of a placeholder, which is exactly wrong for the one block on the page that has a product behind
   it.

   Three layers, reusing the pieces built for the package menu on /services/: the room photo through
   .band-photo, a scrim through .band-scrim, and an amber glow rising from the bottom edge so the
   card looks lit from inside rather than outlined. The border goes solid for the same reason a
   dashed one had to go.

   The dashed default it overrides is not dead weight — see the note on .promo above. */
.promo-members {
  position: relative; overflow: hidden;
  border-style: solid; border-color: hsl(38 90% 55% / .28);
  background: hsl(38 90% 55% / .05);
}
.promo-members .band-photo { opacity: .17; object-position: 50% 42%; }
.promo-members .band-scrim {
  background:
    radial-gradient(120% 80% at 50% 108%, hsl(38 90% 55% / .16), transparent 62%),
    linear-gradient(to bottom, hsl(30 5% 6% / .84), hsl(30 5% 6% / .72) 45%, hsl(30 5% 6% / .86));
}
/* Content above both layers. The wrapper exists only for this — see front-page.php. */
.promo-body { position: relative; }
.promo h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.85rem, 4vw, 2.5rem); margin-bottom: 1rem; }
.promo p { color: var(--muted-foreground); font-size: 1.0625rem; max-width: 40rem; margin: 0 auto 2rem; line-height: 1.7; }

/* The account's amenities as bullets — Randy, 2026-08-06. They were one long comma-spliced sentence
   naming four features, which is a list wearing a paragraph's clothes.

   ⚠️ THE LIST IS CENTRED; THE TEXT INSIDE IT IS NOT. .promo centres everything, and centred list
   items give every line a ragged left edge with the markers floating off it — the one shape that
   makes a short list genuinely hard to scan. So the <ul> is centred as a block and its contents are
   left-aligned against a marker column.

   ⚠️ ONE COLUMN AT EVERY WIDTH. Four amenities in a 2x2 grid is the equal-boxes shape Randy has
   turned down repeatedly. */
.promo-list {
  list-style: none; margin: 0 auto 2rem; padding: 0;
  max-width: 40rem; text-align: left; display: grid; gap: .75rem;
}
.promo-list li {
  position: relative; padding-left: 1.5rem;
  color: var(--muted-foreground); font-size: 1.0625rem; line-height: 1.65;
}
/* The same .55rem amber dot .flow-plain uses, so the site has one bullet rather than two. */
.promo-list li::before {
  content: ""; position: absolute; left: 0; top: .55rem;
  width: .5rem; height: .5rem; border-radius: 50%; background: var(--primary);
}

/* The paid tier, lifted out of the free account's bullet list on 2026-08-07 — Randy did not want a
   block headed "Free to join" listing a perk you have to pay for. It was the fifth <li>.

   A hairline above it, not a fifth dot: the divider is the whole point, because it is what stops the
   sentence reading as one more thing the free account gets. Colour, size and line-height come from
   .promo p above; .promo is in the selector only to beat that rule's margin. The negative top margin
   eats half of .promo-list's 2rem so the line sits with the list it qualifies, not adrift below it. */
.promo .promo-tier {
  max-width: 40rem; margin: -.5rem auto 2rem; padding-top: 1.25rem;
  border-top: 1px solid hsl(38 90% 55% / .22);
}

/* .promo-beats — the beat store strip on /membership/ — was deleted on 2026-08-07 along with the
   block it styled. The dashboard preview took that slot. What the store was promising the producers
   is recorded on the 'Access to the EchoNest beat store' row in inc/content.php, not here. */

/* ---- interior page hero ------------------------------------------------ */

.page-hero { position: relative; height: 45vh; min-height: 320px; overflow: hidden; }
.page-hero.short { height: 34vh; min-height: 240px; }
.page-hero img { width: 100%; height: 100%; object-fit: cover; }
.page-hero .overlay { position: absolute; inset: 0; background: linear-gradient(to bottom, hsl(30 5% 6% / .55), hsl(30 5% 6% / .5), var(--background)); }
.page-hero .caption { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 0 1.5rem; }
.page-hero .caption .loc { font-size: .75rem; text-transform: uppercase; letter-spacing: .3em; color: var(--primary); font-weight: 500; margin-bottom: .75rem; }
.page-hero .caption h1 { font-family: var(--font-display); font-weight: 700; color: #fff; font-size: clamp(2.5rem, 6vw, 3.5rem); }
.page-hero .caption .sub { color: var(--secondary-foreground); margin-top: .75rem; font-size: 1.0625rem; }

/* ---- gear list --------------------------------------------------------- */

.gear-groups { display: grid; grid-template-columns: 1fr; gap: 2.5rem; }
.gear-group h3 {
  font-size: .8125rem; text-transform: uppercase; letter-spacing: .18em; color: var(--primary);
  font-weight: 600; padding-bottom: .75rem; margin-bottom: 1.25rem; border-bottom: 1px solid hsl(30 8% 18% / .6);
}
.gear-group ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; }
/* Every rule is amber; the ordinary items get a faded one and the hero pieces (mic, pres, monitors —
   'hero' => true in inc/content.php) keep it at full strength, so the list reads as one set with the
   standouts still ahead. Was a cool grey /.8 until round 8 (2026-09-02) — the partners wanted the
   faint lines to match the orange ones. */
.gear-group li { padding-left: 1rem; border-left: 2px solid hsl(38 90% 55% / .3); }
.gear-group li.is-hero { border-left-color: var(--primary); }
.gear-group .name { font-weight: 600; font-size: 1rem; }
.gear-group li.is-hero .name { color: var(--foreground); }
.gear-group .note { font-size: .875rem; color: var(--muted-foreground); margin-top: .25rem; line-height: 1.6; }

/* ---- gallery page ------------------------------------------------------ */

.gallery-filters { display: flex; flex-wrap: wrap; justify-content: center; gap: .6rem; margin-bottom: 2.5rem; }
.filter {
  padding: .55rem 1.15rem; border-radius: 9999px; cursor: pointer;
  background: transparent; border: 1px solid var(--border); color: var(--muted-foreground);
  font-family: inherit; font-size: .8125rem; font-weight: 500; transition: all .2s;
}
.filter:hover { color: var(--foreground); border-color: hsl(38 90% 55% / .4); }
.filter.active { background: var(--primary); border-color: var(--primary); color: var(--primary-foreground); }

.photo-grid { display: grid; grid-template-columns: 1fr; gap: .85rem; }
.photo { margin: 0; border-radius: .85rem; overflow: hidden; background: var(--secondary); aspect-ratio: 4/3; cursor: zoom-in; }
.photo[hidden] { display: none; }
.photo img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s ease; }
.photo:hover img { transform: scale(1.05); }
.photo:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
.gallery-note { margin-top: 2.5rem; font-size: .875rem; }

/* Lightbox, built on demand by site.js. */
.lightbox {
  position: fixed; inset: 0; z-index: 200; display: none;
  align-items: center; justify-content: center; padding: 3rem 1.5rem;
  background: hsl(30 5% 4% / .95); backdrop-filter: blur(8px);
}
.lightbox.open { display: flex; }
.lightbox img { max-width: 100%; max-height: 88vh; width: auto; border-radius: .5rem; }
.lightbox button {
  position: absolute; background: hsl(30 5% 12% / .8); border: 1px solid var(--border);
  color: var(--foreground); border-radius: 9999px; cursor: pointer; font-size: 1.5rem;
  width: 2.75rem; height: 2.75rem; display: flex; align-items: center; justify-content: center;
  line-height: 1; transition: background .2s;
}
.lightbox button:hover { background: var(--primary); color: var(--primary-foreground); }
.lightbox .lb-close { top: 1.25rem; right: 1.25rem; }
.lightbox .lb-prev { left: 1.25rem; top: 50%; transform: translateY(-50%); }
.lightbox .lb-next { right: 1.25rem; top: 50%; transform: translateY(-50%); }

/* ---- engineer profiles ------------------------------------------------- */

.eng-profiles { display: grid; grid-template-columns: 1fr; gap: 1.5rem; }
.eng-profile { display: grid; grid-template-columns: 1fr; background: var(--card); border: 1px solid hsl(30 8% 18% / .6); border-radius: 1.25rem; overflow: hidden; transition: border-color .3s; }
.eng-profile:hover { border-color: hsl(38 90% 55% / .35); }
.eng-profile .photo { aspect-ratio: 4/3; border-radius: 0; cursor: default; }
.eng-profile .body { padding: 1.75rem; }
.eng-profile h3 { font-family: var(--font-display); font-size: 1.5rem; font-weight: 700; }
.eng-profile .role { font-size: .8125rem; color: var(--primary); font-weight: 500; margin: .35rem 0 1rem; }
.eng-profile .bio { font-size: .9375rem; color: var(--muted-foreground); line-height: 1.7; margin-bottom: 1.25rem; }
.eng-profile .credits-label { font-size: .6875rem; text-transform: uppercase; letter-spacing: .18em; color: var(--secondary-foreground); font-weight: 600; margin-bottom: .6rem; }
.eng-profile .credits { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.eng-profile .credits li { padding: .3rem .75rem; border-radius: 9999px; background: hsl(30 5% 14%); font-size: .8125rem; color: var(--secondary-foreground); }

/* ---- services ---------------------------------------------------------- */

.service-grid { display: grid; grid-template-columns: 1fr; gap: 1.25rem; }
.service-card { position: relative; padding: 1.75rem; border-radius: 1.25rem; background: var(--card); border: 1px solid hsl(30 8% 18% / .6); display: flex; flex-direction: column; }
.service-card .tag { position: absolute; top: 1.25rem; right: 1.25rem; }
.service-card .ico { width: 2.75rem; height: 2.75rem; border-radius: .75rem; background: hsl(38 90% 55% / .1); display: flex; align-items: center; justify-content: center; font-size: 1.25rem; margin-bottom: 1rem; }
.service-card h3 { font-size: 1.1875rem; font-weight: 600; margin-bottom: .5rem; }
.service-card p { font-size: .9375rem; color: var(--muted-foreground); line-height: 1.65; }
.service-card .price { color: var(--primary); font-weight: 700; font-size: 1rem; margin: 1.25rem 0; }
.service-card .btn { margin-top: auto; }

/* Disabled controls must look unavailable, not merely styled. */
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn:disabled:hover { background: transparent; }

/* ---- honest "this is a preview" notice --------------------------------- */

.notice {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1.5rem;
  margin-top: 3rem; padding: 1.5rem 1.75rem; border-radius: 1rem;
  border: 1px solid hsl(38 90% 55% / .3); background: hsl(38 90% 55% / .06);
}
.notice.notice-lead { margin-top: 0; margin-bottom: 4rem; }
.notice p { font-size: .9375rem; color: var(--secondary-foreground); line-height: 1.65; max-width: 46rem; }
.notice .btn { flex: none; }

/* ---- members: mock dashboard ------------------------------------------- */

.mock { max-width: 52rem; margin: 0 auto; border-radius: 1.25rem; overflow: hidden; border: 1px solid hsl(30 8% 18% / .8); background: var(--card); }
/* ⚠️ .mock-bar IS THE BEST OF THE NEST BRACKET'S FRAME NOW, AND ONLY THAT — 2026-09-03. The
   /membership/ dashboard mock used to wear one too: traffic-light dots, a "Your songs" title and an
   account cluster, a little OS window. Randy cut it — *"we want the info page identical to the real
   deal"* — so that mock is now .dashboard-mock, a bare card matching /my/projects, and the dots rule
   went with it. What is left here is the bracket figure's caption bar (page-best-of-the-nest.php),
   which is a labelled figure and not pretending to be a screen. */
.mock-bar { display: flex; align-items: center; gap: .85rem; padding: .85rem 1.25rem; background: hsl(30 5% 12%); border-bottom: 1px solid hsl(30 8% 18% / .8); }
.mock-bar .mock-title { font-size: .875rem; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mock-bar .tag { margin-left: auto; }

/* The /membership/ dashboard mock, drawn to match one section of the real My Projects screen: the
   amber label sits at the card's left edge (the card is centred and capped at .mock's 52rem), and
   the card carries the same inner padding /my/projects gives its <Card> so the table is not flush to
   the border. No .mock-bar — SongRail on the real screen has no title bar above it. */
.dashboard-mock-label { max-width: 52rem; margin: 0 auto .9rem; text-align: left; }
/* The same inset /my/projects' <Card> uses — p-6 then p-8 at the sm breakpoint. The table keeps its
   own cell padding on top, exactly as it does inside that Card, so the two match. */
.dashboard-mock { padding: 1.5rem; }
@media (min-width: 640px) { .dashboard-mock { padding: 2rem; } }

/* ---- reviews ------------------------------------------------------------ */

/* Quotations, laid out as quotations. Deliberately NOT the four-equal-cards treatment this site has
   rejected twice: reviews vary in length, and forcing them into matching boxes either truncates the
   good one or pads the short one with air. A masonry-ish two-column flow lets each be its own size.

   Empty today — see the note on 'reviews' in inc/content.php for why the block still renders. */
.reviews {
  list-style: none; margin: 0 auto; padding: 0; max-width: 56rem;
  display: grid; gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
}
.review {
  padding: 1.5rem 1.6rem;
  border-left: 2px solid hsl(38 90% 55% / .45);
  background: hsl(30 5% 10% / .5);
  border-radius: 0 .75rem .75rem 0;
}
.review-text { margin: 0; font-size: 1.0625rem; line-height: 1.7; color: var(--foreground); }
.review-by { margin: .9rem 0 0; font-size: .8125rem; color: var(--muted-foreground); }
.review-name { font-weight: 600; color: var(--foreground); }
.review-when::before { content: " · "; }

/* .house-quote is gone with the line it styled — Randy cut the "Michael has incredibly soft hands"
   joke for the launch, and quoting himself in its place was not the answer either (the Google
   listing's only review is his own). The block is the heading and the button now; .review above is
   what real quotes get when there are some. */

/* ---- Best of the Nest: the knockout bracket ---------------------------- */

/* Replaced a four-row poll on 2026-08-06. Randy: *"the image we put on the competition page needs to
   show a tournament bracket style of competition. not the 'here's four things, vote'."* The mechanic
   IS a bracket — sixteen beats, three knockout rounds, two come out — and a list of four rows was
   showing one round of it. The field grew from eight to sixteen on 2026-08-24; see the long comment
   on 'bon_bracket' in inc/content.php before touching the numbers below.

   A bracket cannot reflow. Stacked into one column it is a list, and the narrowing 8 -> 4 -> 2 -> 2 IS
   the information, so below its natural width it scrolls sideways inside its own box.

   contain:paint does the same load-bearing job here as on .compare-scroll (see the long comment
   there): without it Chrome grows the initial containing block to the bracket's full width whenever
   the viewport is narrower, reports the document as ~700px wide at 375, and drags the position:fixed
   float bar out with it. The page never actually scrolls sideways — but the overflow harness reads
   documentElement.scrollWidth and calls it a failure, and so would any real audit. */
.bracket-scroll {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  contain: paint;
  padding: 1.5rem 1.25rem;
}

/* min-height is not decoration. flex:1 on .match can only distribute space that exists, and the
   sixteen rows of card in the first column add up to almost exactly the height the other columns
   need — so without it the eight first-round matchups butt together and read as one 16-item list
   rather than as eight pairings. This is the height at which a pairing is visibly a pairing.

   ⚠️ NOT DOUBLED WITH THE FIELD. The first round doubled from four matchups to eight on 2026-08-24
   (bon_bracket gained a "Sixteen beats" round), and doubling min-height along with it would be
   exactly the "tons of real estate" Randy said this contest should not take. min-width grew to fit
   the new fourth column; min-height grew only enough to keep two beat-rows readable at the tighter
   first-round type below, not enough to keep every column at its old per-row height. */
.bracket { display: flex; gap: 2rem; min-width: 52rem; min-height: 26rem; }

/* ⚠️ FIRST ROUND ONLY, AND THIS IS WHAT KEEPS THE FIGURE FROM GROWING WITH THE FIELD. Eight
   matchups in the space four used to occupy needs a smaller card, not a taller bracket — every
   other round still uses the default .beat-row / .jersey sizing above. */
.bracket-round:first-child .beat-row { padding: .35rem .6rem; font-size: .75rem; }
.bracket-round:first-child .jersey { min-width: 1.15rem; height: 1.15rem; font-size: .625rem; }

/* Equal-width rounds with their slots spread by space-around is what makes the connectors line up
   with no arithmetic anywhere: 4 slots sit at 12.5/37.5/62.5/87.5% and 2 slots at 25/75%, so a
   second-round match is always exactly level with the midpoint of the two that feed it. Change the
   distribution and every connector below stops meeting its match. */
.bracket-round { flex: 1 1 0; display: flex; flex-direction: column; min-width: 9.5rem; }
.bracket-label {
  margin: 0 0 .9rem; text-align: center;
  font-size: .6875rem; text-transform: uppercase; letter-spacing: .18em;
  color: var(--muted-foreground); font-weight: 500;
}
.bracket-slots { flex: 1; display: flex; flex-direction: column; justify-content: space-around; }

/* The slot, not the card. The card is centred inside it, which is what the connectors measure
   against. */
.match { flex: 1; display: flex; align-items: center; position: relative; }

.match-card {
  position: relative; width: 100%; border-radius: .6rem;
  border: 1px solid hsl(30 8% 20% / .9);
  background: hsl(30 5% 11% / .85);
  overflow: hidden;
}

.beat-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: .6rem;
  padding: .5rem .7rem;
  font-size: .8125rem; line-height: 1.35; color: var(--muted-foreground);
}
.beat-row + .beat-row { border-top: 1px solid hsl(30 8% 20% / .9); }
.beat-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The share. Losing the poll lost the one thing that said the public decides this, so every cell
   carries its count — tabular so the two numbers in a matchup line up against each other. */
.beat-pct { flex: none; font-variant-numeric: tabular-nums; font-size: .75rem; opacity: .8; }

/* Advancing. Amber and a weight change, never colour alone — the same beat appears in the next
   column anyway, so the highlight is a convenience rather than the only cue. */
.beat-row.is-through { color: var(--primary); font-weight: 600; }

.match-winner { border-color: hsl(38 90% 55% / .5); background: hsl(38 90% 55% / .08); }
.match-winner .beat-row { padding: .7rem; font-size: .875rem; }

/* ── the connectors ──────────────────────────────────────────────────────
   Three lines per join, and each is on the element that knows its own geometry:
     .match::after        a stub right from this card's centre, into the gap
     .match::before       the vertical joining the two feeders, drawn in the RECEIVING round,
                          spanning the middle 50% of its slot — which is exactly the distance
                          between the two centres feeding it
     .match-card::before  a stub left from the receiving card back to that vertical
   The gap is 2rem, so each stub is 1rem and they meet in the middle. */
.bracket-round:not(.bracket-won) .match::after,
.match::before,
.bracket-round:not(:first-child) .match-card::before {
  content: ""; position: absolute; pointer-events: none;
  border-color: hsl(30 8% 26% / .9);
}

.bracket-round:not(.bracket-won) .match::after {
  right: -1rem; width: 1rem; top: 50%; border-top: 1px solid;
}

.bracket-round:not(:first-child) .match::before {
  left: -1rem; top: 25%; bottom: 25%; border-left: 1px solid;
}
/* The first round has nothing feeding it, so it has no vertical.
   Neither does the output column: each of its two cells is fed by ONE matchup, not two, so a
   vertical there would be a line joining a thing to itself. Horizontal stub only. */
.bracket-round:first-child .match::before,
.bracket-won .match::before { display: none; }

.bracket-round:not(:first-child) .match-card::before {
  left: -1rem; width: 1rem; top: 50%; border-top: 1px solid;
}

.bracket-foot {
  margin: 0; padding: 1.1rem 1.25rem;
  font-size: .875rem; color: var(--muted-foreground); line-height: 1.6;
  background: hsl(30 5% 12% / .5); border-top: 1px solid hsl(30 8% 18% / .8);
}

/* ---- Best of the Nest: on the court (2026-08-24) ------------------------ */

/* Three renderings, shown to Randy as an Artifact; his answer: *"'on the court' looks best to
   me."* Hardwood behind the cards, a seed number in a jersey ring beside every beat, the
   basketball mark standing in for a trophy on the two that go free — layered onto the exact same
   markup the bracket above already runs on. Nothing here touches .bracket / .bracket-round /
   .match / .beat-name / .beat-pct, which is what the harness counts; .jersey, .beat-id and
   .bracket-through-tag are new and additive.

   ⚠️ THE WASH IS A ::after ON .bracket-scroll, NOT A FIFTH .bracket-round. The "8 matchups, then
   4, then 2, then 2 through" assertion reads :nth-child on .bracket's direct children, so anything
   decorative has to live outside that flow — see the long comment in page-best-of-the-nest.php. */
.bracket-fig {
  background:
    radial-gradient(120% 90% at 15% 0%, hsl(38 60% 20% / .18), transparent 60%),
    repeating-linear-gradient(90deg, hsl(28 42% 22%), hsl(28 42% 22%) 3px, hsl(30 40% 19%) 3px, hsl(30 40% 19%) 6px);
}
.bracket-fig .mock-title { color: hsl(40 20% 80%); }

.bracket-fig .bracket-scroll { position: relative; }
/* The free-throw line's paint, behind "Both go free" — roughly the last quarter, the same fraction
   .bracket-round:nth-child(4) actually occupies at 1fr each now that the field grew a column
   (2026-08-24: "Sixteen beats" joined "Last eight" / "Last four" / the won column, four total
   where there were three). Moved from 66%/78% to 75%/84% with it — same proportional ramp, over a
   narrower column. */
.bracket-fig .bracket-scroll::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, transparent 75%, hsl(38 85% 45% / .10) 84%, hsl(38 85% 45% / .14) 100%);
}

.bracket-fig .bracket-label { color: hsl(40 25% 88%); text-shadow: 0 1px 2px hsl(0 0% 0% / .4); }
.bracket-fig .match-card { background: hsl(30 8% 10% / .92); border-color: hsl(38 25% 30% / .6); backdrop-filter: blur(2px); }
.bracket-fig .beat-row + .beat-row { border-top-color: hsl(38 30% 30% / .4); }

/* The jersey — a seed number in a ring, the same idea My Squad's own jersey circles draw with
   initials. Wrapped with .beat-name in .beat-id so .beat-row keeps exactly two flex children
   (the id, the share) and its existing space-between rule still puts the percentage on the right
   without a third item splitting the row. */
.beat-id { display: flex; align-items: baseline; gap: .5rem; min-width: 0; }
/* ⚠️ min-width, NOT width, AND border-radius:999px, NOT 50% — 2026-08-24, when seeds started
   running to sixteen. A fixed 1.4rem circle fit one digit; "16" in the same box either clipped or
   forced the ring oval. min-width keeps a single digit reading as a circle (nothing forces it
   wider) while letting two digits grow the pill instead of being crushed. */
.jersey {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  min-width: 1.4rem; height: 1.4rem; padding: 0 .2rem; border-radius: 999px;
  border: 1.5px solid var(--primary); color: var(--primary);
  font-family: var(--font-brand); font-size: .75rem; font-weight: 700;
}

/* The two that go free — a plate rather than a bare row, the basketball mark in place of a
   trophy since neither beat has a winner yet; that is decided later, on songs. */
.match-winner .beat-row {
  flex-direction: column; align-items: center; justify-content: center; gap: .4rem;
  padding: 1.1rem .75rem; text-align: center;
}
.match-winner .beat-name { font-family: var(--font-display); font-weight: 700; font-size: 1rem; white-space: normal; }
.match-winner .squad-ball svg { width: 2.25rem; height: 2.25rem; }
.bracket-through-tag {
  font-size: .625rem; text-transform: uppercase; letter-spacing: .14em; font-weight: 600;
  color: var(--primary);
}

/* ---- membership: the plan comparison ----------------------------------- */

/* A table, not two pricing cards. A comparison across a fixed set is the one job a table does better
   than any other layout — the eye travels one row and the difference is on the same line, instead of
   holding one bordered list in your head while reading the second. Two equal boxes is also the exact
   shape rejected twice on this site. */

/* Horizontal scroll rather than a reflow to blocks. A comparison that stacks is not a comparison any
   more: the two plans stop sharing a row and the reader is back to remembering. Three columns fit
   above ~560px; below that the paid column peeks and invites the swipe. */
/* contain: paint is doing real work here, not decoration. Without it Chrome grows the initial
   containing block to the table's full 34rem whenever the viewport is narrower than that, which
   reports as a 499px-wide document at 375 and drags every position:fixed element (the floating Book
   bar is left:0;right:0) out to that width with it. The page never actually scrolls sideways —
   body.scrollWidth stays 375 and window.scrollX cannot leave 0 — but the overflow harness reads
   documentElement.scrollWidth and calls it a failure, and so would any real audit.
   overflow-x:auto alone does NOT stop it; contain:paint does, and it is simply the truth about this
   box: a scroll container does contain its own paint. */
.compare-scroll {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  contain: paint;
  margin: 0 -1rem; padding: 0 1rem;
}
.compare { width: 100%; min-width: 34rem; border-collapse: collapse; }

.compare thead td { border: 0; }
.compare thead th {
  padding: 1rem 1.25rem 1.25rem; text-align: center; vertical-align: bottom;
  border-bottom: 1px solid hsl(30 8% 18% / .6);
}
.compare .plan-name {
  display: block; font-size: .6875rem; text-transform: uppercase; letter-spacing: .12em;
  color: var(--muted-foreground); font-weight: 600; margin-bottom: .5rem;
}
.compare .plan-price {
  display: block; font-family: var(--font-display); font-size: 1.875rem; font-weight: 700;
  line-height: 1; color: var(--foreground);
}
.compare .plan-price .per { font-size: .875rem; font-weight: 500; color: var(--muted-foreground); }

/* The paid column is tinted down its whole height so the eye can follow it without a border box.
   This is the one place amber is allowed to mark a column — it is the subject of the page, not
   chrome competing with Book Now. */
.compare .is-paid { background: hsl(38 90% 55% / .05); }
.compare thead th.is-paid { border-bottom-color: hsl(38 90% 55% / .35); }
.compare thead th.is-paid .plan-price { color: var(--primary); }

.compare tbody th {
  padding: .95rem 1.25rem; text-align: left; font-weight: 500; font-size: .9375rem;
  color: var(--secondary-foreground); line-height: 1.5;
}
.compare tbody td { padding: .95rem 1.25rem; text-align: center; font-size: 1.1rem; }
.compare tbody tr + tr th, .compare tbody tr + tr td { border-top: 1px solid hsl(30 8% 18% / .4); }
.compare tbody .yes { color: var(--primary); }
.compare tbody .no { color: hsl(30 8% 28%); }

/* Where the plans differ by degree rather than yes/no. A bare check would hide the actual difference:
   the beat store row is "15% off", not just a checkmark, and the monthly master row (round 7,
   2026-08-28: no longer "stackable, never expire") still needs a cell of its own rather than a tick. */
.compare .cell-note {
  display: block; font-size: .8125rem; line-height: 1.45; color: var(--muted-foreground);
  font-family: var(--font-sans);
}
.compare .is-paid .cell-note { color: var(--secondary-foreground); }

.compare-foot {
  margin: 1.75rem auto 0; max-width: 44rem; text-align: center;
  font-size: .9375rem; line-height: 1.7; color: var(--muted-foreground);
}
.compare-foot a { color: var(--primary); }

/* The three info pages, linked in one line under the table. Deliberately quieter than the paragraphs
   above it — it is a way out of the page for somebody who wants detail, not a third call to action
   competing with the two plan buttons. The middots are decorative and aria-hidden in the markup;
   the wrap-friendly gap is what keeps three link names from stacking badly at 320px. */
.compare-more { display: flex; flex-wrap: wrap; justify-content: center; gap: .35rem .55rem; font-size: .875rem; }
.compare-more a { text-decoration: underline; text-underline-offset: .2em; }
.compare-more span { color: hsl(30 8% 30%); }

/* ---- membership: list price vs member price ---------------------------- */

/* Reuses .tier from the online-services pricing so the two priced comparisons on the site share one
   visual language. The only addition is the pair of numbers on the right. */
.rate-compare { max-width: 46rem; }
.rate-pair { gap: .65rem; font-size: 1.25rem; }
.rate-list { color: var(--muted-foreground); text-decoration: line-through; text-decoration-thickness: 1px; }
.rate-arrow { color: hsl(30 8% 30%); font-size: 1rem; }
.rate-member { color: var(--primary); }

/* ---- membership: the engineer add-on disclosure ------------------------ */

/* Native <details>, so it is keyboard-operable and opens with JS off. The summary is styled as a
   full-width row that matches .tier, because it sits directly under the rate list and should read
   as one more line of the same pricing block — just one that opens. */
/* SCOPED TO details. This block used to be written as a bare `.eng-addon`, and it was silently
   restyling a different component: the "Add a Professional Engineer" strip under the pricing cards on
   the front page is a <div class="eng-addon"> with padding: 1.5rem (see the base rule near the top of
   this file). The unscoped override zeroed that padding, which pushed the +$35 hard against the
   border and clipped the r in "/hr" — Randy, 2026-08-03: *"the R in 'hr' is getting chopped."*
   Two different components must not share one selector; if this disclosure ever needs a class of its
   own, give it one rather than widening this back.

   padding:0 is not redundant with the global * reset — <details> resolves to 24px here regardless,
   which pushed the open panel 2px past the viewport at 320 and (because the float bar is fixed
   left:0/right:0) dragged that bar out with it. Zeroing it explicitly puts the document back to
   exactly 320. Do not delete this as dead. */
details.eng-addon { max-width: 46rem; margin: .6rem auto 0; padding: 0; }
.eng-toggle {
  /* width:100% is required, not tidy-up. A <summary> set to display:flex shrinks to fit its content
     (367px against the 736px block here), so without it the bar sits stranded above a full-width
     panel and the joined top/bottom radii do not line up. */
  display: flex; width: 100%; align-items: center; gap: 1rem;
  padding: 1.15rem 1.5rem; border-radius: .9rem;
  background: var(--card); border: 1px solid hsl(30 8% 18% / .6);
  cursor: pointer; list-style: none;
  transition: border-color .2s;
}
.eng-toggle::-webkit-details-marker { display: none; }
.eng-toggle:hover { border-color: hsl(38 90% 55% / .35); }
.eng-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.eng-toggle-label { font-weight: 600; font-size: 1.0625rem; flex: 1; min-width: 0; }
.eng-toggle-price {
  flex: none; font-family: var(--font-display); font-size: 1.375rem; font-weight: 700;
  color: var(--primary); line-height: 1;
}

/* The open/closed affordance. A chevron drawn in CSS rather than an SVG icon: the bar is the only
   place in this theme with an icon budget, and one more entry in echonest_icon_paths() for a
   decorative caret would be carried on every page that never uses it. */
.eng-toggle-mark {
  flex: none; width: .5rem; height: .5rem; margin-left: .25rem;
  border-right: 2px solid var(--muted-foreground); border-bottom: 2px solid var(--muted-foreground);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .2s ease;
}
.eng-addon[open] .eng-toggle-mark { transform: rotate(-135deg) translate(-2px, -2px); }
.eng-addon[open] .eng-toggle { border-color: hsl(38 90% 55% / .35); border-radius: .9rem .9rem 0 0; }

.eng-addon-body {
  padding: 1.25rem 1.5rem 1.5rem;
  border: 1px solid hsl(38 90% 55% / .2); border-top: 0; border-radius: 0 0 .9rem .9rem;
  background: hsl(30 5% 9% / .5);
}
.eng-addon-intro { font-size: .875rem; color: var(--muted-foreground); line-height: 1.65; margin-bottom: 1.25rem; }

/* .pick-grid goes to 4 columns at desktop, which is right inside the 72rem wrap on /services/ and
   impossible inside this 46rem disclosure — four cards there have a min-content of ~915px and burst
   the panel. Cap it at 2 so the block stays aligned with the rate rows above it. */
@media (min-width: 640px) {
  .eng-addon-body .pick-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ---- membership: the plan CTAs ----------------------------------------- */

/* In <tfoot> so each button stays locked under its own column — the table already owns that
   geometry and a sibling row would have to re-derive it. */
.compare tfoot td { padding: 1.5rem 1.25rem .5rem; text-align: center; vertical-align: top; }
.compare tfoot .cta-lead {
  text-align: left; font-size: .6875rem; text-transform: uppercase; letter-spacing: .12em;
  color: hsl(30 8% 30%); font-weight: 600;
}
.btn-plan { width: 100%; justify-content: center; }
.btn-plan:disabled { opacity: .5; cursor: not-allowed; }

.upgrade-path { margin-top: 1rem; }

/* Upgrade is not a third way in — it is what you do once you are already in — so it sits on its own
   line under the two entry controls rather than beside them. */
/* .auth-row is declared further down this file with the shorthand `margin: 0 auto`, which at equal
   specificity beat a bare `.auth-upgrade { margin-top }` and flattened the gap — Upgrade sat welded
   to the pair above it, which is the opposite of the point (it is a separate step, not a third way
   in). Qualified so it wins wherever the two rules end up in the file. */
.auth-row.auth-upgrade { max-width: 24rem; margin-top: .75rem; }
.auth-upgrade .btn { flex: 1 1 auto; }
.auth-upgrade .btn:disabled { opacity: .5; cursor: not-allowed; }

/* .tier holds label and price on one line, which works on /services/ where the price is a single
   number. Here the right-hand side is a PAIR — "$25/hr -> $21.25/hr" — and the two together do not
   fit beside the label on a phone; at 375 the row ran 36px past the viewport. Stack it instead of
   shrinking the type: the strike-through and the member price both have to stay readable, since
   they are the entire point of the section. */
@media (max-width: 640px) {
  .rate-compare .tier { flex-direction: column; align-items: flex-start; gap: .65rem; }
  .rate-compare .rate-pair { font-size: 1.125rem; }
}

/* Sign In and Register sit as a pair of equal-width buttons — neither is the default action, because
   which one you want depends entirely on whether you have been here before. */
.auth-pair { max-width: 24rem; }
.auth-pair .btn { flex: 1 1 9rem; }
.auth-pair .btn:disabled { opacity: .5; cursor: not-allowed; }

/* On a phone the table must FIT, not scroll. At 34rem the paid column — the entire subject of the
   page — sat completely off-screen behind a half-cut "Free", and a partially visible column is a
   weak invitation to swipe: most people simply never see the price. Tightening the padding and type
   brings all three columns inside 375px, so the comparison is doing its job the moment it appears.
   Below ~360px it still scrolls, which is the right trade at that width; the scroll container stays
   in place for exactly that case. */
@media (max-width: 640px) {
  /* 19rem, not 20rem, and the difference is the whole test. .compare-scroll is a bleed container —
     margin: 0 -1rem with matching padding — so at a 375px viewport its CONTENT box is 312px, not the
     344px it measures. A 20rem (320px) floor is 8px wider than the room it is given, which put the
     table into a permanent 8px scroll at exactly the width this block exists to make it fit. The
     columns need nowhere near 19rem to lay out: min-content across all three is about 220px, so this
     is a floor with real headroom rather than a number tuned to today's copy. */
  .compare { min-width: 19rem; }
  .compare thead th { padding: .75rem .35rem 1rem; }
  .compare tbody th { padding: .8rem .5rem .8rem 0; font-size: .8125rem; }
  .compare tbody td { padding: .8rem .35rem; }
  .compare .plan-name { font-size: .5625rem; letter-spacing: .06em; }
  .compare .plan-price { font-size: 1.375rem; }
  .compare .plan-price .per { font-size: .6875rem; }
  .compare .cell-note { font-size: .6875rem; line-height: 1.35; }

  /* The CTA buttons set the table's minimum width, so .btn's stock 2rem side padding and 1rem type
     pushed it to 446px against 359px of room — the whole comparison started scrolling again purely
     because of two labels. Tighten them and let the text wrap onto a second line. */
  .compare tfoot td { padding: 1rem .3rem .25rem; }
  .btn-plan { padding: .7rem .5rem; font-size: .75rem; border-radius: 1rem; text-align: center; }
  .compare tfoot .cta-lead { font-size: .5625rem; letter-spacing: .04em; }
}

/* ---- the dashboard preview's track table ---------------------------------
   Redrawn 2026-08-21, shipped in its own commit after the app it pictures: one row per song, and
   the file that feeds each stage lives in that stage's own cell — the beat cell is gone, the beat
   lives inside Recorded. Before that it was rebuilt 2026-08-07 as a grid of ✓/○ glyphs that happened
   to contain progress rather than a picture of progress.

   THE RAIL IS DRAWN BY THE CELLS. Each .stage carries a ::before that reaches back across the gap
   to the previous node; .is-done on the CELL lights that segment, fills that node and reveals the
   checkmark inside it. So the graphic is a function of the data — flip 'mixed' to false in
   content.php and the second segment goes dim on its own, with no class list to keep in sync.

   Two consequences worth knowing before editing:
     - The nodes must stay centred in equal-width cells or the segments will not meet them. That is
       what the fixed percentage widths below are for; do not swap them for auto.
     - .is-now is the furthest completed stage in its row, and it is the only thing on the whole
       picture that draws the eye somewhere specific. One per row, decided in the template. */

.progress-table { width: 100%; border-collapse: collapse; }
.progress-table th, .progress-table td { padding: 1rem .75rem; text-align: center; font-size: .875rem; }
/* Top-aligned so the node row stays a constant height whatever hangs under it — see the note on
   .stage::before below. Round 8: the rail line was crossing the file pill. */
.progress-table td { vertical-align: top; }
.progress-table th {
  font-size: .6875rem; text-transform: uppercase; letter-spacing: .12em;
  color: var(--muted-foreground); font-weight: 600;
  border-bottom: 1px solid hsl(30 8% 18% / .6);
}
.progress-table .col-song, .progress-table .song { text-align: left; }
.progress-table .col-song { width: 22%; }
.progress-table .col-stage { width: 26%; }
/* Breathing room against the frame. The last cell takes it on BOTH sides even though only the right
   is doing any work: the node is centred in its content box, so padding on one side alone walks it
   off-centre and the final rail segment comes out visibly shorter than the others. */
.progress-table th:first-child, .progress-table td:first-child { padding-left: 1.25rem; }
.progress-table th:last-child, .progress-table td:last-child { padding-right: 1.25rem; padding-left: 1.25rem; }
.progress-table tbody tr + tr td { border-top: 1px solid hsl(30 8% 18% / .4); }
.progress-table .song { font-weight: 600; }

/* The chip a file becomes, wherever one is offered — a beat, a multi-track, a chosen master. */
.beat-chip {
  display: inline-flex; align-items: center; gap: .45rem; max-width: 100%;
  padding: .35rem .7rem; border-radius: 2rem;
  background: hsl(30 5% 11%); border: 1px solid hsl(30 8% 20% / .9);
}
.beat-chip svg { width: .85rem; height: .85rem; color: var(--primary); flex: none; }
.beat-name {
  font-size: .8125rem; color: var(--foreground); letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The rail. .node is the marker; ::before is the segment reaching back to the node before it. */
.progress-table .stage { position: relative; }
.progress-table .stage .node {
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  width: .7rem; height: .7rem; margin: 0 auto;
  border-radius: 50%; background: var(--card);
  border: 2px solid hsl(30 8% 26%);
  transition: none;
}
/* Always drawn — see the PHP loop above. Invisible until .is-done reveals it, so the rail stays a
   function of the data rather than something the template branches on. */
.progress-table .stage .node-check {
  width: .4rem; height: .4rem; color: var(--primary-foreground); opacity: 0;
}
.progress-table .stage.is-done .node-check { opacity: 1; }
/* Real clearance under the node so the rail line cannot cross the pill. */
.progress-table .stage-file { display: block; margin-top: 1.1rem; }
.progress-table .stage-file .beat-chip { margin: 0 auto; }

/* ⚠️ PINNED TO THE NODE (td padding-top 1rem + half of a .7rem node = 1.35rem from the cell top),
   not `top: 50%`. With `vertical-align: top` on the cell that offset is constant; `50%` tracked the
   row height and slid the line onto the file pill. */
.progress-table .stage::before {
  content: ""; position: absolute; z-index: 0;
  top: 1.35rem; right: 50%; width: 100%; height: 2px; margin-top: -1px;
  background: hsl(30 8% 22%);
}
/* The first stage has nothing to its left to reach back to — its segment would cross the song
   column. The rail starts at its own node instead.
   Matched on "the stage after the song cell" rather than :first-of-type — :first-of-type counts
   <td>s, so it selects the song cell and silently does nothing. */
.progress-table .song + .stage::before { display: none; }

.progress-table .stage.is-done .node { background: var(--primary); border-color: var(--primary); }
.progress-table .stage.is-done::before { background: var(--primary); }

/* Where the record actually is. A ring rather than a bigger dot: size changes make the row look
   misaligned, a halo does not. */
.progress-table .stage.is-now .node { box-shadow: 0 0 0 4px hsl(38 90% 55% / .18); }

/* The stage name only appears in the stacked phone layout below, where the header row is gone. */
.progress-table .stage-label { display: none; }

/* THE FOLD. Even four columns inside a 52rem frame starts crowding once a stage cell also carries a
   filename, and well before 375px it is the widest thing on the document — the overflow the harness
   sweeps for at 320 and 375. So each song becomes its own card: title, then the rail underneath with
   its stages named and their files under them, since the header row is gone.

   The row is a 3-column grid and the stages auto-place one per column, which is what keeps the
   nodes equally spaced — and therefore keeps the ::before segments meeting them — with no second
   set of widths to maintain. */
@media (max-width: 620px) {
  .progress-table, .progress-table tbody, .progress-table tr, .progress-table td { display: block; }
  .progress-table thead { display: none; }

  .progress-table tbody tr {
    display: grid; grid-template-columns: repeat(3, 1fr);
    padding: 1.15rem 1.25rem 1.35rem;
  }
  .progress-table tbody tr + tr { border-top: 1px solid hsl(30 8% 18% / .4); }
  .progress-table tbody tr + tr td { border-top: 0; }

  /* The row carries the padding now, so the per-cell edge padding above has to come off — left on,
     it would push the last node off the centre of its column. */
  .progress-table td,
  .progress-table td:first-child,
  .progress-table td:last-child { padding: 0; }
  .progress-table .song { grid-column: 1 / -1; font-size: .9375rem; margin-bottom: 1.4rem; }

  .progress-table .stage { text-align: center; }
  .progress-table .stage-label {
    display: block; margin-bottom: .5rem;
    font-size: .625rem; text-transform: uppercase; letter-spacing: .1em;
    color: var(--muted-foreground);
  }
  /* The segment has to clear the labels, so it hangs off the node's row rather than the cell's
     middle. .7rem node, 2px line — .35rem from the node's top centres it. */
  .progress-table .stage::before { top: auto; bottom: .35rem; margin-top: 0; }
  .progress-table .stage-file { font-size: .8125rem; }
  .progress-table .stage-file .beat-name { max-width: 6.5rem; }
}

/* ---- the sign-up / account shell --------------------------------------- */

.signup { text-align: center; padding: 3rem 2rem; border-radius: 1.5rem; background: var(--card); border: 1px solid hsl(30 8% 18% / .6); }
.signup .tag { margin-bottom: 1.25rem; }
/* h1 as well as h2 because /sign-in/ has no .page-hero to carry its heading — the panel is the page.
   There is no global h1 rule in this stylesheet; every one is scoped, so an h1 that misses this
   selector falls all the way through to the browser default. */
.signup h1, .signup h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.75rem, 4vw, 2.25rem); margin-bottom: 1rem; }
.signup > p { color: var(--muted-foreground); max-width: 34rem; margin: 0 auto 2rem; line-height: 1.7; }
/* Was .fake-form. Renamed once the controls stopped being decoration: it is a centred row of
   account actions, and the old name told whoever wires up auth that these were props. */
.auth-row { display: flex; flex-wrap: wrap; gap: .75rem; justify-content: center; max-width: 30rem; margin: 0 auto; }

/* THERE ARE NO FIELD STYLES IN THIS STYLESHEET, AND THERE IS NOTHING FOR THEM TO STYLE. .auth-fields,
   .field, .field-label and .field input lived here until 2026-08-09 and dressed the theme's only two
   <input>s, on /sign-in/. Both came out when accounts became real; the fields they styled are in the
   member app at /my, built in Tailwind against the same tokens. Nothing in themes/echonest can
   submit anything, and the assertions in harness.html and mirror-staging.py hold it there. */

/* The pair sits narrower than the .auth-row default so the panel reads as one control group. */
.section-sign-in .auth-pair { max-width: 22rem; }
.section-sign-in .auth-pair .btn { flex: 1; }
.signup .fine { font-size: .75rem; color: var(--muted-foreground); margin-top: 1rem; font-style: italic; }
.signup .alt { font-size: .9375rem; color: var(--muted-foreground); margin-top: 1.75rem; }
.signup .alt a { color: var(--primary); }

/* ---- about: routes onward ---------------------------------------------- */

.next-grid { display: grid; grid-template-columns: 1fr; gap: 1.25rem; }
.next-card { border-radius: 1rem; overflow: hidden; background: var(--card); border: 1px solid hsl(30 8% 18% / .6); transition: border-color .25s, transform .25s; }
.next-card:hover { border-color: hsl(38 90% 55% / .4); transform: translateY(-3px); }
.next-card img { width: 100%; aspect-ratio: 16/10; object-fit: cover; }
.next-card .body { padding: 1.25rem 1.5rem 1.5rem; }
.next-card h3 { font-family: var(--font-display); font-size: 1.25rem; font-weight: 700; margin-bottom: .35rem; }
.next-card p { font-size: .875rem; color: var(--muted-foreground); line-height: 1.6; }

/* ---- contact: finding us ------------------------------------------------ */

.find-us { margin-top: 3.5rem; padding-top: 3rem; border-top: 1px solid hsl(30 8% 18% / .4); }
.find-us h2 { font-family: var(--font-display); font-size: 1.75rem; font-weight: 700; margin-bottom: 1.75rem; text-align: center; }

/* ---- footer additions --------------------------------------------------- */

/* The logo is portrait; v1's 51px height left it ~41px wide and unreadable. */
footer.site img.logo { height: 96px; width: auto; }

/* The footer link row grew from four links to six. v1 only taught the wp_nav_menu
   variant (ul.menu) to wrap, so the no-menu fallback — which is what the site
   actually ships with — overflowed the viewport on a phone. Both wrap now. */
footer.site .links { flex-wrap: wrap; justify-content: center; row-gap: .75rem; }
footer.site .copy a { color: var(--muted-foreground); margin-left: .75rem; text-decoration: underline; }
footer.site .copy a:hover { color: var(--primary); }

/* ---- footer: the link row is its own band ------------------------------ */

/* Round 8 (2026-09-02): the partners saw the link row wrapping to two or three lines. It was the
   right-hand column of .cols, boxed into half the footer's width beside the logo and address.
   footer.php moves it out to a full-width band of its own between .cols and .copy; given the whole
   width the nine links hold one line on a laptop. Both markup shapes carry .foot-links now — the
   shipped fallback <div class="links foot-links"> and, if a menu is ever assigned, the
   container_class — so centring lives here and both inherit it. flex-wrap stays as the phone
   fallback; it just no longer fires at laptop widths. */
footer.site .foot-links {
  width: 100%; margin: 2rem auto 0;
  justify-content: center; flex-wrap: wrap; row-gap: .75rem;
}
footer.site .foot-links ul.menu { justify-content: center; flex-wrap: wrap; }

/* ---- online services: how it works ------------------------------------- */

/* A sequence, drawn as a sequence. The previous version was three cards in a row, which reads as
   three things happening at once — the generic template look, and wrong for a process where every
   step waits on the one before it. Stacked at ALL widths on purpose: it never goes multi-column,
   because the moment it does, reading order stops being the order things happen in. The rule joining
   the numbers is what carries "then", and it is the whole point of the component. */
.flow { list-style: none; margin: 0 auto; padding: 0; max-width: 42rem; counter-reset: flow; }
.flow li {
  position: relative; counter-increment: flow;
  display: grid; grid-template-columns: 2.5rem 1fr; gap: 1.25rem;
  padding-bottom: 2.5rem;
}
.flow li:last-child { padding-bottom: 0; }

/* The step number. */
.flow li::before {
  content: counter(flow);
  grid-column: 1; z-index: 1;
  width: 2.5rem; height: 2.5rem; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: hsl(38 90% 55% / .12); color: var(--primary);
  border: 1px solid hsl(38 90% 55% / .4);
  font-weight: 700; font-size: .9375rem; font-variant-numeric: tabular-nums;
}

/* ⚠️ .flow-plain — the same single column WITHOUT the numbers, for lists that are not sequences.
   Added 2026-08-05 for /gear-rentals/, where numbering turned the rentable categories into a ranking
   of equipment published by the studio, with somebody's own gear at number one. See the "categories
   only" rule in page-gear-rentals.php.
   Keeps the connector rule, because the single column is still the point; only the counter goes. */
.flow-plain li::before {
  content: ""; width: .55rem; height: .55rem; margin: .95rem 0 0 .975rem;
  background: var(--primary); border-color: transparent;
}

/* The connector: from this number down to the next. Fades out so the last leg does not look cut. */
.flow li::after {
  content: ""; position: absolute; left: 1.25rem; top: 2.5rem; bottom: .35rem;
  width: 1px; transform: translateX(-.5px);
  background: linear-gradient(to bottom, hsl(38 90% 55% / .4), hsl(30 8% 22% / .9));
}
.flow li:last-child::after { display: none; }

/* ⚠️ .flow-side — the two alternatives ACROSS rather than down. Randy, 2026-08-06: *"two ways to
   share a block section shows the two ways stacked vertically. id lke the present them left/right."*
   The stacked version was deliberate (a single column says "two ways of using one thing" where two
   boxes say "pick one of these products") and this is the compromise that keeps that reading while
   doing what was asked: the two halves sit side by side but get NO card of their own — no border, no
   background, no equal-height box. They are two columns of one thing, divided by a hairline.

   ⚠️ DO NOT PUT A BORDER OR A BACKGROUND ON THESE. That is the "wix blocks" layout Randy has
   rejected before, and side-by-side is one rule away from becoming it.

   The divider re-uses the flow connector rather than adding an element: same ::after, stood up on the
   first item's right edge instead of running down from its dot. */
.flow-side { max-width: 52rem; display: grid; grid-template-columns: 1fr 1fr; }
.flow-side li { display: block; padding: 0 2.5rem; }
.flow-side li:first-child { padding-left: 0; }
.flow-side li:last-child { padding-right: 0; }
/* The marker moves above the title — beside it, a .55rem dot in a two-column layout just looks like
   a stray bullet the text failed to clear. */
.flow-side li::before { margin: 0 0 .9rem; }
.flow-side li::after {
  left: auto; right: 0; top: .2rem; bottom: .2rem; width: 1px; transform: none;
  background: linear-gradient(to bottom,
    hsl(30 8% 22% / 0), hsl(38 90% 55% / .35) 18%, hsl(30 8% 22% / .9) 70%, hsl(30 8% 22% / 0));
}
.flow-side .flow-body { padding-top: 0; }

/* One column again where two will not fit, and the divider lies down with them. */
@media ( max-width: 40rem ) {
  .flow-side { grid-template-columns: 1fr; }
  .flow-side li, .flow-side li:first-child, .flow-side li:last-child { padding: 0 0 1.6rem; }
  .flow-side li + li { padding-top: 1.6rem; }
  .flow-side li::after {
    left: 0; right: 0; top: auto; bottom: 0; width: auto; height: 1px;
    background: linear-gradient(to right, hsl(38 90% 55% / .3), hsl(30 8% 22% / 0));
  }
}

.flow-body { padding-top: .3rem; min-width: 0; }
.flow-title { display: block; font-weight: 600; font-size: 1.125rem; }
.flow-desc { display: block; color: var(--muted-foreground); line-height: 1.7; margin-top: .4rem; }

/* ---- online services: pricing tiers ------------------------------------ */

/* A printed menu, not a table of cards. The rows were four bordered boxes on an opaque --card fill;
   they are now open lines separated by hairlines, so the photograph behind the section (.band-shot)
   reads through the whole block instead of showing only in the gutters. Nothing else changed: the
   count is on the left, the price on the right, at every width.

   Do not give these rows a background. The section's own scrim is what keeps them legible, and a
   filled row would hide the picture and put the boxes back. */
.tiers { display: grid; max-width: 46rem; margin: 0 auto; }
.tier {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding: 1.15rem .25rem;
  border-bottom: 1px solid hsl(30 8% 24% / .55);
}
.tier:last-child { border-bottom: 0; }
/* No :hover on .tier. It used to lift the border, back when each row ended in a button; nothing in a
   row is interactive now and a highlight that follows the cursor promises otherwise. */
.tier-band { min-width: 0; }
.tier-label { font-weight: 600; font-size: 1.0625rem; }
.tier-note { font-size: .8125rem; color: var(--muted-foreground); margin-top: .15rem; }
.tier-price {
  flex: none; font-family: var(--font-display); font-size: 1.75rem; font-weight: 700;
  color: var(--primary); line-height: 1; display: flex; align-items: center; gap: .6rem;
}
/* THE ONE ROW THAT IS NOT A MIX PACKAGE. Randy asked for Stereo Mastering to read as its own thing
   while staying on the menu, so it is set apart the way a menu sets apart a section it does not want
   confused with the mains: a gap, an amber hairline instead of the grey one, and the name in the
   display serif the section headings use rather than the sans the track counts are set in.

   No box, no badge, no second column — those would make it a different component. It is the same row,
   in a different voice. Everything below is deliberately restrained: the price stays identical to the
   other four so the eye still reads straight down the right-hand edge. */
.tier.tier-aside {
  margin-top: 1.4rem; padding-top: 1.75rem;
  border-top: 1px solid hsl(38 90% 55% / .28);
}
/* One rule between the packages and the aside, not two. Without this the last package keeps its own
   grey bottom border and the gap reads as a double line. :has is the honest selector here; where it
   is unsupported the only cost is that second hairline. */
.tiers .tier:has(+ .tier-aside) { border-bottom: 0; }
.tier-aside .tier-label {
  font-family: var(--font-display); font-weight: 700; font-size: 1.55rem;
  line-height: 1.1; letter-spacing: .01em;
}
/* There was a short amber rule under the name here and Randy cut it, 2026-08-03 — the serif, the gap
   and the divider above the row already do the separating, and a second mark on the same row started
   competing with them. Do not add it back. */
.tier-aside .tier-note { font-style: italic; margin-top: .55rem; }

.tier-tbd { color: hsl(30 8% 30%); }
/* The 49+ row says "Contact for quote" where the others say a figure. At 1.75rem display type that
   string is wider than the label column it sits beside and bursts the row on a phone, so it drops to
   body type — it is a sentence, not a number, and should not be shouting like one either. */
.tier-quote { font-family: var(--font-sans); font-size: .9375rem; font-weight: 600; line-height: 1.3; }
.tier-terms { font-style: italic; }
/* .tier-foot is gone with the line it styled — round 7.3 (2026-08-28), Randy cut the Prices
   section's closing sentence outright rather than asking for a reword. */

/* .tier-buy and .tier-cta are gone with the buttons they held — the packages are a menu now, so the
   price is the row's second child directly.

   The row also no longer stacks on a phone. It stacked because label + note beside a price AND a
   button was 378px of content in a 320px viewport; without the button it fits, and a menu line reads
   better as name-left/price-right at every width. Only the gap tightens. Measured at 320 and 375
   in the overflow sweep — if you put anything back in this row, re-measure before trusting it. */
@media (max-width: 640px) {
  .tier { gap: 1rem; }
}

/* The before/after section that lived here is gone (Randy, 2026-08-03: "the entire before and after
   section we don't need"). Its .ab-* rules went with it — if it ever returns it returns with real
   audio and a real player, not placeholders. */

/* ---- online services: choose an engineer ------------------------------- */

/* Every card is the same shape on purpose — nobody can outgrow anyone by writing more, and the
   running order rotates daily in echonest_engineers_rotated(). See inc/content.php. */
.pick-grid { display: grid; grid-template-columns: 1fr; gap: 1rem; }
.pick-card {
  display: flex; flex-direction: column; gap: .9rem;
  padding: 1.5rem; border-radius: 1rem;
  background: var(--card); border: 1px solid hsl(30 8% 18% / .6);
  transition: border-color .2s;
}
.pick-card:hover { border-color: hsl(38 90% 55% / .35); }
.pick-head { display: flex; align-items: center; gap: 1rem; }
.pick-photo { width: 3.5rem; height: 3.5rem; border-radius: 50%; object-fit: cover; flex: none; }
.pick-card h3 { font-size: 1.0625rem; font-weight: 600; }
.pick-tags { list-style: none; margin: .35rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: .3rem; }
.pick-tags li {
  font-size: .6875rem; padding: .15rem .5rem; border-radius: 999px;
  background: hsl(38 90% 55% / .1); color: var(--primary); white-space: nowrap;
}
.pick-pitch { font-size: .875rem; color: var(--muted-foreground); line-height: 1.6; flex: 1; }
/* Credits at the foot of the card, replacing the "Rig &amp; credits →" link out to /engineers/.
   ⚠️ CHIPS, NOT A MUTED LINE — Randy, 2026-08-21: the dot-joined sentence was "tucked away and
   boring"; this borrows .pick-tags' own amber pill, sized up, so a credit reads as evidence rather
   than a caption. .pick-pitch is now optional furniture (see functions.php), so this is often the
   last thing on the card and gets a little breathing room above it. */
.pick-credits { margin-top: .35rem; }
.pick-credits-label {
  font-size: .6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: .1em;
  color: var(--muted-foreground); margin: 0 0 .5rem;
}
.pick-credits-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem; }
.pick-credits-list li {
  font-size: .8125rem; font-weight: 600; line-height: 1.3; white-space: nowrap;
  padding: .3rem .7rem; border-radius: 999px;
  background: hsl(38 90% 55% / .12); color: var(--primary); border: 1px solid hsl(38 90% 55% / .25);
}
/* The is-tbd fallback keeps the old, quieter treatment — it is a staging note between the partners,
   never something a visitor sees (echonest_show_tbd()), and dressing it as a chip would make a
   placeholder look like a real credit. */
p.pick-credits.is-tbd { font-size: .8125rem; color: var(--muted-foreground); line-height: 1.5; margin-top: .35rem; }

/* This engineer's own rates, foot of their /services/ card — repricing round, 2026-08-24.
   Rows, not chips: a price reads better as text on its own line than crammed into a pill. */
.pick-rates { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid hsl(30 8% 18% / .6); }
.pick-rate-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; }
.pick-rate-list li {
  display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
  font-size: .875rem;
}
.pick-rate-label { font-weight: 600; }
.pick-rate-note { display: block; font-weight: 400; font-size: .75rem; color: var(--muted-foreground); }
.pick-rate-price { font-weight: 600; color: var(--primary); white-space: nowrap; }
/* The mastering row sits apart from the mixing rows the same way Stereo Mastering used to sit apart
   on the old /services/ menu (.tier-aside) — a hairline, not a box, so it still reads as this
   engineer's own card and not a second table. */
.pick-rate-mastering { margin-top: .35rem; padding-top: .5rem; border-top: 1px dashed hsl(30 8% 18% / .6); }

/* .pick-any is gone — first de-styled from an amber callout, then deleted outright on 2026-08-03
   ("remove the selected at checkout thing altogether"). The roster now ends on the last card. The
   fairness sentence it carried is stated on /engineers/; the note in page-services.php explains what
   moved and what must never replace it. */

/* A note pinned to an unbuilt block for the partners, in Randy's own voice — staging only, never
   production (echonest_show_tbd()). Italic and quoted so it cannot be mistaken for site copy, and
   .staging-note is the hook the harness uses to exclude it from the first-person sweep. */
.staging-note { font-style: italic; color: hsl(38 60% 70%); }

/* ---- the SquadUp block (staging only) -------------------------------- */

/* Rendered only under echonest_show_tbd(), so this never ships as it stands — see the long note in
   page-membership.php. Built from the same parts as everything else (dashed .promo border, amber, the
   display face) rather than as a pastiche of anybody's team badge: the artwork is original, and the
   basketball is the joke. Do not restyle this toward the Space Jam lockup. */
.squad {
  text-align: center; padding: 2.5rem 2rem; border-radius: 1.5rem;
  border: 1px dashed hsl(38 90% 55% / .35); background: hsl(38 90% 55% / .04);
}
.squad h2 { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.6rem, 3.5vw, 2.1rem); margin-bottom: 1rem; }
.squad p { color: var(--muted-foreground); font-size: 1.0625rem; max-width: 42rem; margin: 0 auto 1rem; line-height: 1.7; }

/* The badge: ball, then wordmark, on one baseline. The ball is drawn at 2.25rem because the waveform
   seam disappears at icon size. */
.squad-badge { display: inline-flex; align-items: center; gap: .6rem; margin-bottom: 1.25rem; }
.squad-ball { display: inline-flex; color: var(--primary); }
.squad-ball svg { width: 2.5rem; height: 2.5rem; }
.squad-word {
  font-family: var(--font-display); font-weight: 700; font-size: 1.75rem;
  color: var(--primary); letter-spacing: .01em; line-height: 1;
}

/* /squadup/'s own crest, 2026-08-25 — the drawn badge (ball + arced wordmark baked in as pixels)
   replacing the ball+text pair on this one page. .squad-badge-crest is additive to .squad-badge
   rather than a replacement for it, so page-membership.php's smaller .squad-split teaser is
   untouched by this rule. */
.squad-badge-crest { margin-bottom: 1.5rem; }
.squad-crest-img { width: 9.5rem; height: 9.5rem; display: block; margin: 0 auto; }
@media (max-width: 30rem) {
  .squad-crest-img { width: 8rem; height: 8rem; }
}

/* Both of these have to outrank `.squad p`, which sets the muted colour — hence the element in the
   selector. Without it the rule line and the footnote render as ordinary body copy. */
.squad p.squad-rule { font-weight: 600; color: var(--foreground); margin-bottom: 0; }

/* ---- .squad-split: the /membership/ block, asymmetric ------------------- */

/* Randy, 2026-08-07: *"having the same design stye style on the member benefits page for squadup
   and then beats by the team right below it looks pretty boring."*

   He is right. This block and the beat store strip under it were the same recipe — centred, 1.5rem
   radius, dashed amber border, small mark over a display h2 over a muted paragraph — and the account
   card below them is a third centred box at the same width. Three in a row read as one component
   repeating. The answer is three different SHAPES, not three variations on a card: see the note in
   page-membership.php, and [[echonest-no-template-blocks]] for why a grid was never on the table.

   The store block is gone as of 2026-08-07 and the dashboard preview stands in that slot, so the run
   down the page is now this block, a browser-chrome frame and a centred card — three shapes still,
   and by a wider margin than before. Nothing here needs undoing on that account.

   ⚠️ THIS MODIFIER IS SCOPED TO /membership/. Plain .squad above is untouched, and .squad-badge is
   shared with the lede on /squadup/ — every rule here that touches the badge is written under
   .squad-split so the other page keeps its inline ball-then-word lockup. */
.squad-split {
  display: grid; grid-template-columns: auto 1fr; gap: 2.5rem; align-items: center;
  text-align: left;
  /* Solid, not dashed. Dashed is this site's placeholder shape (see .promo); this discount is agreed
     and only the booking app is missing, so it must not be drawn as a stub. It used to be stated as
     a contrast with the dashed store block beside it — that block is gone, and the rule holds on its
     own: nothing that is decided gets a placeholder border. */
  border-style: solid; border-color: hsl(38 90% 55% / .28);
  /* isolate, not just relative: the glow below sits at z-index -1 so it paints above this block's
     own background and below its text, and without a stacking context here it would escape behind
     the section instead. */
  position: relative; isolation: isolate; overflow: hidden;
}
/* The same lit-from-inside idea as .promo-members, thrown from under the mark so it rises behind the
   artwork rather than washing the paragraph.

   ⚠️ IT IS DELIBERATELY NARROW. The first pass was 78% wide from 8%/118% at .18 and, on a block this
   short and this wide, that is not a glow — the falloff never completes before the right edge and the
   whole panel goes evenly brown. Keeping the source small and close to the corner is what makes it
   read as light rather than as a dirty background. */
.squad-split::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(38% 150% at 12% 112%, hsl(38 90% 55% / .2), transparent 68%);
}
/* The mark stacks and grows: it is the only dedicated artwork on the site and it was reading as a
   decorative bullet at 2.5rem above a centred heading. The hairline is the divider .flow-side uses. */
.squad-split .squad-badge {
  flex-direction: column; gap: .5rem; margin: 0;
  padding-right: 2.5rem; border-right: 1px solid hsl(38 90% 55% / .18);
}
.squad-split .squad-ball svg { width: 4.5rem; height: 4.5rem; }
.squad-split .squad-word { font-size: 1.5rem; }
.squad-split h2 { margin-bottom: .85rem; }
/* .squad p centres itself with `margin: 0 auto` — undone here, or the sentence floats away from the
   heading it belongs to. */
.squad-split p { margin: 0; max-width: 36rem; }

/* One column when the two will not sit side by side. The mark keeps its own line and the divider
   turns horizontal rather than disappearing — it is what separates the lockup from the pitch. */
@media (max-width: 46rem) {
  .squad-split { grid-template-columns: 1fr; gap: 1.5rem; justify-items: center; text-align: center; }
  .squad-split .squad-badge {
    padding-right: 0; border-right: 0;
    padding-bottom: 1.5rem; border-bottom: 1px solid hsl(38 90% 55% / .18);
    align-self: stretch;
  }
  .squad-split p { margin: 0 auto; }
}
/* .squad-open is gone with the red staging warning it styled — Randy wanted the section to explain
   the two rates rather than caveat itself. The gate in page-membership.php is what keeps the block
   off production now; nothing on the page says so. */

/* ---- the bench, on /squadup/ ----------------------------------------- */

/*
 * ⚠️ A REAL SCREENSHOT, NOT A HAND-BUILT MOCK — round 7.3 (2026-08-28), replacing everything that
 * used to be here (.squad-court, .court-modes, .court, .bench, .player*, .jersey, .hour-step, the
 * squad-in animation). Rounds 6 and 7 rebuilt that hand-drawn approximation of Bench.jsx twice and it
 * still wasn't close enough — Randy: *"just put the damn screenshot in there properly cropped."* This
 * is that screenshot, cropped to the card, decorative like the /membership/ dashboard preview rather
 * than operable like the mock it replaces.
 */
.squad-shot { max-width: 32rem; margin: 0 auto; text-align: center; }
.squad-shot img {
  width: 100%; height: auto; display: block;
  border-radius: 1.25rem; box-shadow: 0 1.5rem 3rem -1rem hsl(0 0% 0% / .5);
}
.squad-shot figcaption {
  margin-top: 1rem; font-size: .8125rem; color: var(--muted-foreground);
}

/* ---- engineer profiles: pitch and rig ---------------------------------- */

.eng-profile .pitch { color: var(--secondary-foreground); font-size: .9375rem; line-height: 1.7; margin-top: .75rem; }
.eng-profile .rig-label,
.eng-profile .credits-label {
  font-size: .6875rem; text-transform: uppercase; letter-spacing: .18em;
  color: var(--muted-foreground); margin-top: 1.25rem; margin-bottom: .5rem;
}
.eng-profile .rig { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem; }
.eng-profile .rig li {
  font-size: .8125rem; padding: .3rem .7rem; border-radius: .5rem;
  background: hsl(30 5% 12% / .8); border: 1px solid hsl(30 8% 20% / .8);
}
.eng-request { display: inline-block; margin-top: 1.25rem; font-size: .8125rem; font-weight: 600; color: var(--primary); }
.eng-request:hover { color: hsl(38 90% 65%); }

/* ⚠️ THE SELECTOR HAS TO BE `.cta p.cta-alt`, NOT `.cta-alt`. This paragraph is a `.cta p`, and
   `.cta p` (0-1-1, up near the top of this file) outranks a bare `.cta-alt` (0-1-0) whatever the
   source order — so a plain `.cta-alt { margin-top }` silently loses and the only space under the
   button is line-box leading. Round 8 (2026-09-02): the partners asked for real air here. */
.cta p.cta-alt { margin-top: 2rem; font-size: .875rem; color: var(--muted-foreground); }
.cta-alt a { color: var(--primary); text-decoration: underline; }

/* ---- floating book button ---------------------------------------------- */

/* Covers the stretches of a long page where neither the header's Book Now (scrolled away) nor a
   section CTA (not reached yet) is on screen. site.js tucks it away whenever a [data-book-anchor]
   IS visible, so it fills the gaps between the real buttons rather than stacking on top of them.

   Default state is visible. With no JS, or no IntersectionObserver, it simply stays on — the
   failure mode is one redundant button, never a page with no way to book. */
.book-float {
  position: fixed; z-index: 40;
  right: 1.5rem; bottom: 1.5rem;
  display: inline-flex; align-items: center; justify-content: center;
  padding: .85rem 1.6rem; border-radius: 999px;
  background: var(--primary); color: var(--primary-foreground);
  font-weight: 600; font-size: .9375rem; line-height: 1; white-space: nowrap;
  box-shadow: 0 8px 28px hsl(30 5% 2% / .55), 0 0 0 1px hsl(38 90% 55% / .25);
  transition: opacity .25s ease, transform .25s ease, background .2s ease;
}
.book-float:hover { background: hsl(38 90% 60%); color: var(--primary-foreground); }
.book-float .bf-short { display: none; }

/* Tucked, not removed: it keeps its place in the tab order only when it is actually reachable. */
.book-float.is-tucked {
  opacity: 0; transform: translateY(.75rem) scale(.96);
  pointer-events: none; visibility: hidden;
}

/* Never over the open drawer, and never over an open lightbox. */
body.drawer-open .book-float { display: none; }

/* Phone: a full-width bar along the bottom, which is where a thumb already is. The body gets
   matching bottom padding so the bar can never cover the last line of the footer. */
@media (max-width: 640px) {
  .book-float {
    left: 0; right: 0; bottom: 0; border-radius: 0;
    padding: 1rem 1.5rem;
    padding-bottom: max(1rem, env(safe-area-inset-bottom));
    box-shadow: 0 -6px 24px hsl(30 5% 2% / .5);
    font-size: 1rem;
  }
  body { padding-bottom: 4.25rem; }
  .book-float.is-tucked { transform: translateY(100%); }
}

/* Narrow phones: the short label, so the bar never forces a horizontal scroll. */
@media (max-width: 360px) {
  .book-float .bf-label { display: none; }
  .book-float .bf-short { display: inline; }
}

/* The /membership/ dashboard mock's account cluster (.mock-account, .ma-ico, .ma-avatar) was
   deleted on 2026-09-03 with the rest of that mock's fake window chrome — the real My Projects
   screen has no bell/message/avatar row above the table. The message panel and mastering drop zone
   that once sat here went earlier, on 2026-08-07. See the dashboard section in page-membership.php. */

/* ---- v2 responsive ------------------------------------------------------ */

@media (max-width: 900px) {
  .nav-links { display: none; }
  .menu-toggle { display: inline-flex; }
}

/* On a narrow phone the wordmark, the Book Now button and the hamburger together
   were wider than the viewport, which pushed the whole document sideways — every
   section then rendered clipped, not just the bar. Shrinking the wordmark keeps
   the booking CTA visible, which is the one thing that must not be dropped. */
@media (max-width: 560px) {
  .sitebar, .sitebar.over-hero, .sitebar.over-hero.scrolled { padding-left: 1rem; padding-right: 1rem; }
  .sitebar .inner { gap: .75rem; }
  .sitebar .brand-mark { font-size: 1.1rem; letter-spacing: .05em; }
  .btn-sm { padding: .55rem 1rem; font-size: .8125rem; }
  .notice, .notice.notice-lead { padding: 1.25rem; }
  .sitebar .bar-actions { gap: .4rem; }
  .account { width: 2rem; height: 2rem; }
  .account svg { width: 1.05rem; height: 1.05rem; }
}

/* At 320px the account control was the 44px that pushed the bar — and therefore the whole
   document — past the viewport. The corner belongs to Book Now on the narrowest phones, so the
   account glyph steps aside; Members is still one tap away in the drawer, which carries its own
   copy of it precisely for this. Measured, not guessed: 320px overflowed by 44px, 375px had 55px
   of headroom, so the cut sits between them. */
@media (max-width: 380px) {
  .account { display: none; }
}

@media (min-width: 640px) {
  .card-grid { grid-template-columns: repeat(2, 1fr); }
  .amenity-grid { grid-template-columns: repeat(2, 1fr); }
  .hood-grid { grid-template-columns: repeat(2, 1fr); }
  .photo-grid { grid-template-columns: repeat(2, 1fr); }
  .service-grid { grid-template-columns: repeat(2, 1fr); }
  .next-grid { grid-template-columns: repeat(3, 1fr); }
  .gear-groups { grid-template-columns: repeat(2, 1fr); }
  .cf-steps { grid-template-columns: repeat(2, 1fr); column-gap: 2rem; }
  .cf-body { padding: 2.25rem 2.25rem 2.5rem; }
  .pick-grid { grid-template-columns: repeat(2, 1fr); }
  .service-grid.two { grid-template-columns: repeat(2, 1fr); }
  .eng-profile { grid-template-columns: 15rem 1fr; }
  /* ⚠️ THE HEADSHOT MUST NOT SET THE CARD'S HEIGHT. It did until 2026-08-09: the photo sized itself
     from the image's own proportions, so the four cards came out 289 / 295 / 322 / 242 tall — four
     different heights for four identical profiles, decided by whoever cropped the photo — and while
     the bios are still to come each one held 55px of text in 290px of card.

     So the image is taken out of flow and the column carries a floor instead. min-height is 4:3 at
     this column width; the photo is a stretched grid item, so a card that grows past the floor when
     an engineer's bio lands takes the photo with it. Do not put aspect-ratio back on this — that
     pins the height and the photo stops following the text. */
  .eng-profile .photo { aspect-ratio: auto; height: auto; min-height: 11.25rem; position: relative; }
  .eng-profile .photo img { position: absolute; inset: 0; }
  /* Name and role sit in the middle of the card rather than at the top of an empty one. Once the
     bios arrive the body is the taller side and this stops mattering, which is why it is safe. */
  .eng-profile .body { align-self: center; }
  .strip .shot { width: 21rem; height: 15rem; }
}

@media (min-width: 1024px) {
  .card-grid { grid-template-columns: repeat(3, 1fr); }
  .amenity-grid { grid-template-columns: repeat(3, 1fr); }
  /* .hood-grid stays at two columns (set at 640px). It went to three for a fifth "Finding us" spot
     that no longer exists — with four items three columns is the lopsided 3+1 the partners flagged
     in round 8 (2026-09-02), so 2×2 it is, matching .contact-grid directly above it. */
  .photo-grid { grid-template-columns: repeat(3, 1fr); }
  .service-grid { grid-template-columns: repeat(4, 1fr); }
  .gear-groups { grid-template-columns: repeat(3, 1fr); }
  .split { grid-template-columns: 1fr 1fr; gap: 4rem; }
  .pick-grid { grid-template-columns: repeat(4, 1fr); }

  /* Four cards is one per engineer, so the store's two must NOT inherit the 4-up service grid. */
  .service-grid.two { grid-template-columns: repeat(2, 1fr); max-width: 44rem; margin-inline: auto; }

}

/* ---- the signal chain becomes one composite block ----------------------- */

/* THE OVERLAY STARTS AT 900, not 1024. Above this the photograph IS the block — it fills the whole
   frame and the copy sits on the dark left half of it; below, the two stack and the photo is a strip
   above the text, because pale type over a dark frame at phone width is unreadable no matter how
   heavy the scrim.

   It moved down from 1024 on 2026-08-03. A browser window a little under 1024 — which is where Randy
   was reading it — fell back to the stacked version, and that is the layout he was describing as not
   large enough: a 16:9 band with 500px of text underneath instead of one composite design. */
@media (min-width: 900px) {
  .chain-feature { min-height: 44rem; display: flex; align-items: flex-end; }
  .chain-feature .cf-media { position: absolute; inset: 0; aspect-ratio: auto; }
  /* Only the left column needs to be dark enough to read on. The right half is left almost clear so
     the rack, the monitors and the ON AIR neon are actually visible — they are the argument the copy
     is making, and the first version scrimmed them into a black rectangle. */
  .cf-scrim {
    background:
      linear-gradient(to right, hsl(30 5% 6% / .93) 0%, hsl(30 5% 6% / .8) 38%, hsl(30 5% 6% / .3) 68%, hsl(30 5% 6% / .08) 100%),
      linear-gradient(to bottom, hsl(30 5% 6% / .35) 0%, hsl(30 5% 6% / 0) 35%, hsl(30 5% 6% / .45) 100%);
  }
  .cf-body { position: relative; padding: 2.75rem; max-width: 38rem; }
  .cf-steps { grid-template-columns: repeat(2, 1fr); column-gap: 2rem; }
}

/* Full width, the block gets its full height and the copy column can breathe. 50rem against a block
   that is now up to 96rem wide keeps the frame near 2:1 — any shorter and the photograph reads as a
   letterbox strip again, which is what this pass was fixing. */
@media (min-width: 1200px) {
  .chain-feature { min-height: 50rem; }
  .cf-body { padding: 3.5rem; max-width: 50rem; }
  .cf-steps { column-gap: 2.5rem; }
}

/* The fixed admin bar sits above the fixed site header. */
.admin-bar .sitebar.over-hero { top: 32px; }
@media screen and (max-width: 782px) {
  .admin-bar .sitebar.over-hero { top: 46px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}
