﻿/* ============================================================
   layout.css — the app shell and the page containers.

   The workbench: material on the left, instrument on the right,
   result spanning the full width underneath. The ribbon dock is
   the only thing allowed to touch both edges of the viewport —
   that full bleed is what encodes "long-form" structurally, so it
   never sits inside a max-width container.
   ============================================================ */

/* ---- app shell ------------------------------------------------
   Desktop: the shell fills the viewport and never scrolls itself;
   only the two panes inside the workbench scroll, independently.
   Below 900px this reverts to normal whole-page scrolling, because
   two nested scroll boxes stacked on a phone is worse than one. */
.app-shell { display: flex; min-height: 100vh; align-items: stretch; }

.app-rail {
  flex: none;
  width: var(--rail-w);
  /* Sticky, not just a tall flex child: on the studio screen the body never
     scrolls (see .screen-studio below) so this is inert there, but on
     library/queue - ordinary pages that scroll normally - a merely-tall
     rail would scroll away with the table under it. Nav that disappears
     partway down a list isn't persistent nav. */
  position: sticky; top: 0;
  height: 100vh;
  display: flex; flex-direction: column; align-items: stretch;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-2);
  background: var(--surface);
  border-right: var(--border-width) solid var(--line);
}
.rail-logo {
  display: flex; align-items: center; gap: var(--space-3);
  height: 40px; padding: 0 var(--space-2);
  margin-bottom: var(--space-2);
  border-radius: var(--radius-input);
  text-decoration: none; color: var(--tx);
}
.rail-logo:hover { background: var(--surface-sunken); }
.rail-divider {
  height: 1px; background: var(--line);
  margin: 0 var(--space-2) var(--space-2);
}
.rail-nav, .rail-utility { display: flex; flex-direction: column; gap: 2px; }
.rail-spacer { flex: 1; }

/* Label beside the icon, not stacked under it: the row reads as one
   line at normal text size instead of a 10px caption. */
.rail-item {
  display: flex; flex-direction: row; align-items: center; gap: var(--space-3);
  width: 100%; padding: var(--space-2) var(--space-3);
  border: none; background: none;
  color: var(--tx-muted); text-decoration: none; cursor: pointer;
  border-radius: var(--radius-input);
  font-family: var(--font-body);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  text-align: left;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.rail-item .icon { width: 18px; height: 18px; }
.rail-item:hover { background: var(--surface-sunken); color: var(--tx); }
.rail-item.is-active {
  background: var(--signal-bg); color: var(--signal-text);
  font-weight: var(--weight-medium);
}
:root[data-theme="dark"] .rail-item.is-active { color: var(--signal); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .rail-item.is-active { color: var(--signal); }
}

.app-main {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column;
}

/* ---- top bar -------------------------------------------------- */
.topbar {
  flex: none;
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-3) var(--space-6);
  background: var(--surface);
  border-bottom: var(--border-width) solid var(--line);
}
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: var(--space-4); }

/* ---- workbench ------------------------------------------------
   The grid spans the full width so the settings pane sits against
   the right edge. Reading width is capped on the editor's content
   instead — capping the shell would strand the pane mid-screen on
   a wide display. */
.workbench {
  flex: 1; min-height: 0; min-width: 0;
  display: grid; grid-template-columns: minmax(0, 1fr) 340px;
}
.workbench > .work-main { order: 1; min-width: 0; overflow-y: auto; }
.workbench > .work-side {
  order: 2; min-width: 0; overflow-y: auto;
  background: var(--surface);
  border-left: var(--border-width) solid var(--line);
}
.work-main > .pane > * { max-width: 860px; margin-inline: auto; }

.pane { padding: var(--space-6); }
.pane + .pane { border-top: var(--border-width) solid var(--line); }
.pane > h2 {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--tx-muted);
  margin-bottom: var(--space-4);
}

/* ---- ribbon dock ---------------------------------------------
   Fixed to the foot of the working area, not the viewport. It
   starts where the rail ends so the rail's own controls stay
   reachable while something is playing - a player that covers the
   navigation is a player you have to close to use the app.

   The direction asks for a full-bleed ribbon touching both edges.
   That was overruled deliberately: it sat on top of the rail. The
   width is now driven by --rail-w so the two can never disagree. */
:root { --rail-w: 208px; }

.ribbon-dock {
  position: fixed; left: var(--rail-w); right: 0; bottom: 0; z-index: 50;
  height: 84px;
  display: none;
  flex-direction: column;
  background: var(--surface);
  border-top: var(--border-width) solid var(--line);
}
.ribbon-dock.is-open { display: flex; }
.ribbon-dock[hidden] { display: none; }

/* Row 1 - the scrub track. Fixed at a compact height rather than flex:1: the
   track itself is a 4px line drawn at the row's vertical centre (see
   render() in ribbon.js, which reads the canvas's own height and always
   centres on it - no JS change needed here), so a tall row bought nothing
   but empty canvas above and below the line, and pushed the whole control
   row down toward the bottom edge to make room for that emptiness. 28px
   still gives a comfortable click/drag target. No horizontal padding: it
   still runs the full width of the working area, just not over the rail. */
.ribbon-slider {
  flex: none; height: 28px;
  display: block; width: 100%;
  cursor: pointer;
}
.ribbon-canvas { display: block; width: 100%; height: 100%; }

/* Row 2 - one control bar in three zones: what is playing on the left, the
   transport dead centre, times and actions on the right. The centre stays
   centred regardless of how long the title is, because the outer zones are
   equal-weight flex children rather than content-sized. flex:1 (rather than
   a fixed height) is what the slider row gave up above: this row now takes
   whatever the dock has left, and align-items:center puts its own content
   in the middle of that - which is the space between the scrub track and
   the bottom edge the control row was supposed to be centred in. */
.ribbon-bar {
  flex: 1; min-height: 0;
  display: flex; align-items: center; gap: var(--space-4);
  padding: 0 var(--space-4);
  font-size: var(--text-2xs);
  color: var(--tx-muted);
}
.ribbon-track, .ribbon-side {
  flex: 1 1 0; min-width: 0;
  display: flex; align-items: center; gap: var(--space-3);
}
.ribbon-track { flex-direction: column; align-items: flex-start; gap: 1px; }
.ribbon-side { justify-content: flex-end; }

.ribbon-title {
  max-width: 100%;
  font-size: var(--text-sm); color: var(--tx);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The claim the product is built on, stated where the proof is drawn. */
.ribbon-note {
  max-width: 100%;
  color: var(--seal);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.ribbon-transport {
  flex: none;
  display: flex; align-items: center; gap: var(--space-2);
}
.ribbon-skip { width: 32px; height: 32px; border-color: transparent; }
.ribbon-time { flex: none; color: var(--tx-muted); }
.ribbon-sep { opacity: 0.5; }
.ribbon-regen { flex: none; }

/* Round because it starts sound - the one place outside the ribbon where
   full roundness is earned. */
.ribbon-play {
  width: 38px; height: 38px; flex: none;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-ribbon);
  border: none;
  background: var(--signal-solid); color: var(--signal-on);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
.ribbon-play:hover { background: var(--signal-solid-hover); }
.ribbon-play .icon { width: 16px; height: 16px; }

/* Both glyphs live in the DOM; only the one matching the state is shown, so
   no JS has to swap markup and the two can never disagree. */
.ribbon-play .icon-pause,
.ribbon-dock.is-playing .ribbon-play .icon-play { display: none; }
.ribbon-dock.is-playing .ribbon-play .icon-pause { display: block; }

body.has-ribbon .workbench > .work-main,
body.has-ribbon .workbench > .work-side { padding-bottom: 84px; }

/* ---- page containers (marketing, prose, auth) ----------------- */
.wrap { max-width: 1080px; margin: 0 auto; padding: 0 var(--space-6); }
/* Where .wrap sits inside .app-main (library, queue), it is a flex item -
   and a flex item with auto left/right margins does not stretch to fill the
   cross axis, spec or no align-items:stretch: auto margins take the free
   space for centering instead, so the item sizes to its own content. A
   six-column table is wide content, so .wrap sized to the table instead of
   the space beside the rail, and took the page sideways with it on exactly
   the range where the table hasn't yet switched to its stacked-card layout.
   width:100% forces the fill explicitly; the max-width/auto-margin pair
   still centers whatever's inside once that 100% is capped. */
.app-main > .wrap { width: 100%; }
.wrap--narrow { max-width: 760px; }
.wrap--prose { max-width: 68ch; }

.site-header {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-6);
  border-bottom: var(--border-width) solid var(--line);
  background: var(--surface);
}
.site-header .site-nav { margin-left: auto; display: flex; align-items: center; gap: var(--space-4); }

.centered-page {
  min-height: 100vh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: var(--space-8) var(--space-5);
}

/* ---- responsive ----------------------------------------------
   768px: the settings pane stacks below the script, and the three tier
   cards - roomy enough side by side in a 545px main column, cramped once
   that column is the full (narrower) viewport width - go to one column.
   Below 560px: the rail drops to icons only, costing 56px not 208. */
@media (max-width: 900px) {
  .app-shell { min-height: 100vh; }
  .app-main { height: auto; }
  .workbench { grid-template-columns: 1fr; }
  .workbench > .work-main, .workbench > .work-side {
    overflow-y: visible; height: auto;
  }
  .workbench > .work-side {
    border-left: none;
    border-top: var(--border-width) solid var(--line);
  }
  body.has-ribbon .workbench > .work-main,
  body.has-ribbon .workbench > .work-side { padding-bottom: 0; }
  body.has-ribbon { padding-bottom: 64px; }
  .tier-selector { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  /* The rail narrows, and the dock follows it because both read --rail-w. */
  :root { --rail-w: 56px; }
  .app-rail { padding: var(--space-2) var(--space-1); align-items: center; }
  .app-rail .wordmark, .rail-item span:not(.sr-only) { display: none; }
  .rail-logo { justify-content: center; padding: 0; }
  .rail-item { justify-content: center; padding: var(--space-2) 0; gap: 0; }
  .rail-item .icon { width: 20px; height: 20px; }
  /* 56px of rail leaves ~300px for the top bar. The credit meter's 190px
     floor plus the upgrade button does not fit, so the meter gives up its
     minimum and takes the width that is left. */
  .topbar { padding: var(--space-2) var(--space-3); gap: var(--space-2); flex-wrap: wrap; }
  .topbar-right { gap: var(--space-2); min-width: 0; flex: 1; }
  .topbar-right .credit-meter { min-width: 0; flex: 1 1 auto; }
  .topbar-right .keys { display: none; }

  .pane { padding: var(--space-5) var(--space-4); }
  .wrap { padding: 0 var(--space-4); }

  /* 64px on a phone. The ribbon keeps its full bleed; what goes is the
     text that repeats what the times already say. */
  .ribbon-dock { height: 64px; }
  /* No explicit height here: .ribbon-bar is flex:1 in the base rule, so it
     already takes whatever the 28px slider leaves (36px) and centres its
     own content in it. Setting a height here too would just contradict the
     flex-basis the shorthand already established. */
  .ribbon-bar { padding: 0 var(--space-2); gap: var(--space-2); }
  .ribbon-title, .ribbon-note { display: none; }
  .ribbon-track { display: none; }
  .ribbon-side { gap: var(--space-2); }
  .ribbon-play { width: 30px; height: 30px; }
  .ribbon-skip { width: 28px; height: 28px; }
  .ribbon-skip .icon { width: 15px; height: 15px; }
  body.has-ribbon { padding-bottom: 64px; }
}
