/* PorchTunes stylesheet.
   Extracted verbatim from index.html's inline <style> block (no rule changes).
   New design-system tokens are layered below the original rules. */

  :root {
    --navy: #1f2333;
    --copper: #e0894f;
    --copper-dark: #b85f2e;
    --rust: #b6432c;
    --text: #f6efe2;
    /* Contrast bumped 0.62 -> 0.72 (2026-08-19, readability pass) - this
       token drives nearly every helper-text/label surface in the app, so
       raising it once here fixes "muted text hard to read on dark
       panels" everywhere at once rather than overriding it per-element. */
    --text-muted: rgba(246, 239, 226, 0.72);
    --glass-bg: rgba(24, 27, 41, 0.55);
    --glass-border: rgba(255, 255, 255, 0.14);
    --chip-bg: rgba(255, 255, 255, 0.07);
    --chip-border: rgba(255, 255, 255, 0.12);
    --card-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
    color-scheme: dark;
  }
  * { box-sizing: border-box; }
  html, body {
    height: 100%;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
  }
  body {
    margin: 0;
    font-family: "Segoe UI", system-ui, sans-serif;
    color: var(--text);
    min-height: 100vh;
    position: relative;
  }
  .bg-photo {
    position: fixed; inset: 0;
    background-image: url('hero-photo.jpg');
    background-size: cover;
    background-position: center 45%;
    filter: blur(1px) saturate(1.2) brightness(1.02) contrast(1.08);
    transform: scale(1.08);
    z-index: -3;
  }
  /* Warm golden-hour cast over the (naturally cool, blue-sky) mountain
     photo, so it reads as sunlit rather than washed-out or moody blue —
     matches the warm copper cast of the mascot's own photo instead of
     clashing with it. */
  .bg-warmth {
    position: fixed; inset: 0;
    background: radial-gradient(circle at 50% 30%, rgba(255,180,110,0.28) 0%, rgba(224,137,79,0.12) 45%, transparent 75%);
    mix-blend-mode: soft-light;
    z-index: -2;
  }
  .bg-scrim {
    position: fixed; inset: 0;
    background: linear-gradient(180deg, rgba(23,26,40,0.42) 0%, rgba(23,26,40,0.15) 25%, rgba(23,26,40,0.65) 100%);
    z-index: -1;
  }
  .page { display: flex; justify-content: center; padding: 24px 16px 110px; }
  .panel {
    width: 100%;
    max-width: 640px;
    display: flex;
    flex-direction: column;
    gap: 16px;
  }
  .glass {
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border-radius: 18px;
    padding: 20px 22px;
  }
  .glass h3 {
    margin: 0 0 12px;
    font-size: 12px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
    color: var(--text-muted);
  }
  .glass h3 .note { text-transform: none; letter-spacing: 0; font-weight: 400; opacity: .75; }
  .section-header { display: flex; align-items: center; justify-content: space-between; margin: 0 0 12px; }
  .section-header h3 { margin: 0; }

  /* ---------- collapsible cards (Instruments, Porch Mixer + Voice,
     Account) - progressive disclosure per the 2026-08-19 hybrid-
     disclosure spec. Instant toggle by design, not an animated height
     transition: height:auto doesn't transition natively in CSS, and
     this codebase has already hit two real WebKit-only bugs this same
     week (Web Audio beat-sync, async-callback handling) - a clean
     instant toggle is the explicitly-preferred fallback over a
     transition that might turn out jumpy/broken specifically on
     iPhone Safari, not tested here. ---------- */
  .card-collapse-btn {
    background: none; border: none; padding: 6px; margin: -6px; border-radius: 8px;
    color: var(--text-muted); cursor: pointer; display: flex; align-items: center; flex: 0 0 auto;
    min-width: 32px; min-height: 32px; justify-content: center;
  }
  .card-collapse-btn svg { width: 16px; height: 16px; transition: transform .15s ease; }
  .is-collapsed .card-collapse-btn svg { transform: rotate(-90deg); }
  .card-summary {
    display: none; font-size: 12.5px; color: var(--text-muted); margin-top: 2px; line-height: 1.5;
  }
  .is-collapsed .collapsible-body { display: none; }
  .is-collapsed .card-summary { display: block; }
  /* Mobile-only collapsibles (Instruments, Account) never collapse on
     desktop at all - the toggle simply doesn't exist there, per the
     spec's "always expanded on desktop, no toggle" requirement for
     these two specifically (Porch Mixer + Voice is the one card that's
     genuinely collapsible on both breakpoints). */
  @media (min-width: 900px) {
    .mobile-only-collapse .card-collapse-btn { display: none; }
    .mobile-only-collapse.is-collapsed .collapsible-body { display: block; }
    .mobile-only-collapse.is-collapsed .card-summary { display: none; }
  }

  .title-block { text-align: center; padding: 6px 0 2px; }
  .brand-row { display: flex; align-items: center; justify-content: center; gap: 8px; }
  .brand-row .wordmark { font-family: Georgia, serif; font-style: italic; font-weight: 700; font-size: 24px; color: var(--copper); }
  /* Restrained on purpose - these are secondary to Make My Song/Customize,
     not a second row of "actions": subdued dark-glass circles with a
     warm-tinted icon, not the bold copper gradient the CTA itself uses. */
  .social-row { display: flex; justify-content: center; gap: 12px; margin-top: 10px; }
  .social-row a {
    color: var(--copper); display: flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 50%;
    background: rgba(255,255,255,0.06); border: 1px solid var(--chip-border);
    transition: transform .15s, background .15s;
  }
  .social-row a:hover { transform: scale(1.08); background: rgba(255,255,255,0.12); }
  .pt-store-row { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
  .pt-store-badge {
    font-size: 11px; color: var(--text-muted); padding: 6px 12px; border-radius: 999px;
    border: 1px solid var(--chip-border); background: rgba(255,255,255,0.04);
  }
  .pt-legal-row { display:flex; justify-content:center; flex-wrap:wrap; gap:6px; text-align:center; font-size:11px; color:var(--text-muted); margin-top:8px; }
  .pt-legal-row a { color:var(--text-muted); text-underline-offset:3px; }
  .pt-legal-row a:hover, .pt-legal-row a:focus-visible { color:var(--text); }
  .tagline { font-size: 14.5px; color: var(--text); margin-top: 3px; font-style: italic; font-weight: 600; }

  .pill-row { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
  .pill {
    display: flex; align-items: center; gap: 6px;
    background: var(--chip-bg); border: 1px solid var(--chip-border);
    backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
    border-radius: 100px; padding: 6px 12px; font-size: 12px; color: var(--text-muted);
  }
  .pill b { color: #fff; font-variant-numeric: tabular-nums; font-weight: 700; }
  .pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--copper); }

  /* ---------- mascot hero card ---------- */
  .hero-card {
    position: relative;
    border-radius: 22px;
    overflow: hidden;
    background: linear-gradient(155deg, var(--copper) 0%, var(--rust) 65%, #7a2c1d 100%);
    box-shadow: 0 18px 34px rgba(184,95,46,0.35), inset 0 1px 0 rgba(255,255,255,0.18);
    padding: 10px;
  }
  /* Same treatment as .hero-card (the mascot card) — used for the
     account/billing card so the two read as siblings, minus any
     generate/play/download controls. */
  .card-frame {
    position: relative;
    border-radius: 22px;
    overflow: hidden;
    background: linear-gradient(155deg, var(--copper) 0%, var(--rust) 65%, #7a2c1d 100%);
    box-shadow: 0 18px 34px rgba(184,95,46,0.35), inset 0 1px 0 rgba(255,255,255,0.18);
    padding: 14px 16px;
  }
  .hero-video-frame {
    border-radius: 15px; overflow: hidden; background: var(--navy);
    aspect-ratio: 4 / 5; position: relative;
  }
  .hero-video-frame video {
    position: absolute; inset: 0; width: 100%; height: 100%;
    /* Full-bleed over letterboxing, per real feedback - the idle and dance
       clips have very different native aspect ratios, so this does crop
       some of each rather than showing empty bars. "center top" keeps his
       head in frame for the landscape-ish idle clip (matches the
       object-position already proven pre-redesign); for the much taller
       dance clips the crop that actually happens is left/right, where
       "top" has no effect and "center" keeps him horizontally centered. */
    object-fit: cover; object-position: center top;
  }
  .hero-video-frame video.is-hidden { display: none; }
  /* Porch Boy: appears via a left-to-right clip-path wipe the first time
     a genre is picked (he's seated in the left portion of the composite
     photo, so the wipe reads as him arriving and settling in rather than
     a generic curtain reveal). Stays in the "revealed" clip state across
     idle/dance toggles afterward - .is-hidden alone (display:none) hides
     him during dancing without re-triggering the wipe when idle returns. */
  #mascotIdleBoy {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; object-position: center top;
    clip-path: inset(0 100% 0 0);
    transition: clip-path 1.3s cubic-bezier(0.22, 1, 0.36, 1);
  }
  #mascotIdleBoy.is-hidden { display: none; }
  #mascotIdleBoy.is-revealed { clip-path: inset(0 0 0 0); }
  /* Genre-matched "standing" still - shown between picking a genre and
     the song actually playing. Toggled often (every genre change, every
     pause), so a quick crossfade rather than the one-time porch-duo
     wipe above. */
  #mascotGenreScene {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; object-position: center top;
    opacity: 0; transition: opacity 0.3s ease;
  }
  #mascotGenreScene.is-hidden { display: none; }
  #mascotGenreScene.is-visible { opacity: 1; }
  .hero-controls { display: flex; flex-direction: column; align-items: stretch; gap: 12px; padding: 10px 4px 8px; }
  .hero-controls audio { width: 100%; }
  .hero-controls-row { display: flex; align-items: center; gap: 8px; width: 100%; }
  .hero-bottom {
    padding: 12px 4px 2px; margin-top: 4px; border-top: 1px solid rgba(255,255,255,0.15);
  }
  @keyframes downloadPulse {
    0%, 100% { box-shadow: 0 6px 16px rgba(184,95,46,0.35), 0 0 0 0 rgba(255,255,255,0.4); }
    50% { box-shadow: 0 6px 20px rgba(184,95,46,0.55), 0 0 0 7px rgba(255,255,255,0); }
  }
  button.secondary.attention-pulse {
    background: linear-gradient(135deg, var(--copper), var(--copper-dark));
    border-color: transparent;
    animation: downloadPulse 1.6s ease-in-out infinite;
  }
  .model-select {
    width: 100%; background: rgba(12,13,20,0.4); color: #fff;
    border: 1px solid rgba(255,255,255,0.14); border-radius: 10px;
    padding: 11px 14px; font-size: 14px; font-weight: 600;
  }
  /* Same flash used for the Download reminder - draws the eye to a
     required-but-unmade choice, stops the instant a real model is
     picked (see the change handler removing this class). */
  .model-select.attention-pulse {
    border-color: transparent;
    animation: downloadPulse 1.6s ease-in-out infinite;
  }
  @media (prefers-reduced-motion: reduce) {
    button.secondary.attention-pulse, .model-select.attention-pulse { animation: none; }
  }

  .textarea-with-mic { position: relative; }
  .textarea-with-mic textarea { padding-right: 58px; }
  .mic-btn {
    position: absolute; bottom: 8px; right: 8px; width: 40px; height: 40px;
    border-radius: 50%; padding: 0; display: flex; align-items: center; justify-content: center;
    font-size: 17px; background: linear-gradient(135deg, rgba(184,95,46,0.28), rgba(184,95,46,0.14));
    border: 1px solid rgba(184,95,46,0.55); color: #fff;
    box-shadow: 0 2px 8px rgba(0,0,0,0.25);
  }
  .mic-btn:hover { background: linear-gradient(135deg, rgba(184,95,46,0.42), rgba(184,95,46,0.22)); border-color: var(--copper); }
  .mic-btn:active { transform: scale(0.95); }
  .mic-btn.listening {
    background: linear-gradient(135deg, var(--rust), #7a2c1d); border-color: transparent;
    animation: micPulse 1.2s ease-in-out infinite;
  }
  @keyframes micPulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(182,67,44,0.5); }
    50% { box-shadow: 0 0 0 6px rgba(182,67,44,0); }
  }
  @media (prefers-reduced-motion: reduce) {
    .mic-btn.listening { animation: none; }
  }

  .switch-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    margin-top: 12px; cursor: pointer;
  }
  .switch-label { font-size: 13px; color: var(--text); margin: 0; }
  .switch-cost {
    display: inline-block; margin-left: 7px; font-size: 10.5px; font-weight: 700;
    letter-spacing: .03em; color: var(--copper); background: rgba(224,137,79,0.14);
    border-radius: 100px; padding: 2px 8px;
  }
  .switch { position: relative; width: 38px; height: 22px; flex: none; }
  .switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; z-index: 1; }
  .switch-track {
    position: absolute; inset: 0; background: rgba(255,255,255,0.12);
    border: 1px solid var(--chip-border); border-radius: 100px; transition: background .15s;
  }
  .switch-track::before {
    content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
    border-radius: 50%; background: #fff; transition: transform .15s;
  }
  .switch input:checked ~ .switch-track {
    background: linear-gradient(135deg, var(--copper), var(--copper-dark)); border-color: transparent;
  }
  .switch input:checked ~ .switch-track::before { transform: translateX(16px); }
  .switch-note { font-size: 12px; color: var(--text-muted); margin-top: 8px; line-height: 1.45; }

  /* Model-aware character budget for Describe Your Song (item 5) - the
     count is of this textarea alone, while the real cap the backend
     enforces applies to the *composed* prompt (genre+mood+instruments+
     details+production combined), so this is an honest approximation,
     not the literal remaining budget - the "shared with..." note below
     makes that explicit once it matters (80%+). */
  .char-count { font-size: 11.5px; color: var(--text-muted); text-align: right; margin-top: 6px; }
  .char-count .char-count-note { display: block; font-size: 11px; opacity: .85; }
  .char-count.warn { color: var(--copper); }
  .char-count.over { color: #ff9d8a; font-weight: 600; }

  main, .card, label, select, input[type=text], textarea, button {
    font: inherit; color: inherit;
  }
  label { display: block; font-size: 13px; color: var(--text-muted); margin-bottom: 8px; line-height: 1.45; }
  #billingCard input::placeholder { color: rgba(255,255,255,.65); }
  input[type=text], input[type=password], textarea, select {
    width: 100%; border: 1px solid var(--chip-border); border-radius: 8px;
    padding: 8px 10px; font: inherit; color: var(--text); background: rgba(255,255,255,0.05);
  }
  /* Native <select> dropdown popups mostly ignore the dark page theme and
     render with the OS/browser's own light popup background - explicitly
     color the <option>s dark-on-light so they're never cream-on-white. */
  select option { color: #1f2333; background: #fffaf3; }
  textarea { resize: vertical; min-height: 90px; }
  .visually-hidden {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0,0,0,0); white-space: nowrap; border: 0; padding: 0; margin: -1px;
  }
  .row { display: flex; gap: 8px; align-items: center; }

  /* min-height 44px on every button (item 9/10: real 44x44 tap targets,
     not just the round Preview button which already had it) - padding
     alone at the old 9px/16px undershot that on typical font metrics. */
  button { border: none; border-radius: 100px; padding: 9px 16px; min-height: 44px; cursor: pointer; transition: filter .12s ease, transform .12s ease; }
  button.primary {
    background: linear-gradient(135deg, var(--copper), var(--copper-dark));
    color: #fff; font-weight: 700;
    box-shadow: 0 6px 16px rgba(184,95,46,0.4);
  }
  button.primary:disabled { background: rgba(255,255,255,0.15); color: rgba(255,255,255,0.4); box-shadow: none; cursor: not-allowed; }
  button.secondary { background: rgba(12,13,20,0.4); color: #fff; border: 1px solid rgba(255,255,255,0.14); }
  button.secondary:disabled { background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.35); cursor: not-allowed; }
  /* Real hover/active feedback (audit finding, 2026-08-18: previously
     none existed beyond browser default, which custom background/border
     on <button> suppresses) - "visibly clickable" per item 9. */
  @media (hover: hover) {
    button.primary:not(:disabled):hover, button.secondary:not(:disabled):hover { filter: brightness(1.12); }
  }
  button.primary:not(:disabled):active, button.secondary:not(:disabled):active { transform: scale(0.98); }
  .round-btn { width: 44px; height: 44px; border-radius: 50%; flex: 0 0 auto; padding: 0; display: flex; align-items: center; justify-content: center; font-size: 16px; }
  /* Generate Song: the app's single most important action - taller,
     bigger type, full-width within its card (item 9). */
  #generateBtn { min-height: 52px; font-size: 16px; }
  #status { font-size: 12.5px; color: rgba(255,255,255,0.85); min-height: 18px; margin-top: 6px; }
  #status.error { color: #ff9d8a; }

  /* Dark charcoal/navy surface with the copper accent reserved for the
     one primary action, not a solid copper strip across the whole banner -
     that read as too close to a generic bright OS-notification color
     block regardless of hue. */
  .update-banner {
    position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
    display: flex; align-items: center; justify-content: center; gap: 14px;
    flex-wrap: wrap; padding: 10px 16px;
    background: linear-gradient(135deg, #171a26, #1f2333);
    border-bottom: 1px solid rgba(224,137,79,0.35);
    color: var(--text); font-size: 13px; box-shadow: 0 2px 10px rgba(0,0,0,0.4);
  }
  .update-banner button {
    background: linear-gradient(135deg, var(--copper), var(--copper-dark));
    color: #fff; font-weight: 600;
    font-size: 12.5px; padding: 6px 14px; border-radius: 100px;
  }
  .update-banner .update-dismiss {
    background: none; color: var(--text-muted); font-size: 18px; line-height: 1; padding: 4px 8px;
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  }

  /* ---------- pre-launch ribbon (PT-LAB-20260901-001) ----------
     Gated by app.js's PUBLIC_PRELAUNCH_ENABLED; fails closed (hidden by
     default - see the [hidden] rule below, needed because the .pt-
     prelaunch-ribbon class's own display:flex would otherwise outrank
     the browser's default [hidden] UA rule, same reason .pt-customize[hidden]
     exists further below). Deliberately normal document flow - never
     position:fixed/absolute - so it only ever pushes .page/.pt-shell
     down; it can never overlay Menu, Account, the logo, headline, Porch
     Buddy, or any form control. Full-bleed by being a body-level
     sibling of .page, outside any max-width panel. */
  .pt-prelaunch-ribbon {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; text-align: center; padding: 13px 16px;
    /* Deliberately rust -> deep-brown only (no lighter copper-dark
       midpoint) - the lighter stop dropped the secondary text's contrast
       to 3.92:1 against white/cream, below WCAG AA's 4.5:1 for normal-
       size text. This two-stop gradient stays >=4.5:1 (measured 4.83:1
       to 8.38:1) at every point along it. */
    background: linear-gradient(100deg, var(--rust) 0%, #7a2c1d 100%);
    border-top: 3px solid #fac05a; border-bottom: 3px solid #fac05a;
    box-shadow: 0 4px 16px rgba(0,0,0,0.35);
  }
  .pt-prelaunch-ribbon[hidden] { display: none; }
  .pt-prelaunch-ribbon-primary {
    font-family: Georgia, "Times New Roman", serif;
    font-weight: 700; font-size: clamp(18px, 4vw, 26px);
    letter-spacing: 0.16em; text-transform: uppercase;
    color: #fff; text-shadow: 0 2px 6px rgba(0,0,0,0.45);
  }
  .pt-prelaunch-ribbon-secondary {
    font-size: clamp(12.5px, 2.6vw, 15px); font-weight: 500;
    color: #fdeee0; max-width: 560px; line-height: 1.4;
  }
  @media (min-width: 700px) {
    .pt-prelaunch-ribbon { flex-direction: row; gap: 14px; padding: 14px 24px; }
    .pt-prelaunch-ribbon-primary::after {
      content: ""; display: inline-block; width: 1px; height: 16px; margin-left: 14px;
      background: rgba(255,255,255,0.4); vertical-align: middle;
    }
  }
  @keyframes ptPrelaunchGlow {
    0%, 100% { box-shadow: 0 4px 16px rgba(0,0,0,0.35); }
    50% { box-shadow: 0 4px 20px rgba(250,192,90,0.4); }
  }
  .pt-prelaunch-ribbon:not([hidden]) { animation: ptPrelaunchGlow 3.6s ease-in-out infinite; }
  @media (prefers-reduced-motion: reduce) {
    .pt-prelaunch-ribbon { animation: none; }
  }

  /* ---------- chips ---------- */
  .chip-row { display: flex; flex-wrap: wrap; gap: 9px; }
  .chip {
    font-size: 13.5px; padding: 7px 14px; border-radius: 100px;
    background: var(--chip-bg); border: 1px solid var(--chip-border);
    color: rgba(246,239,226,0.85); cursor: pointer;
  }
  .chip.active {
    background: linear-gradient(135deg, var(--copper), var(--copper-dark));
    border-color: transparent; color: #fff; font-weight: 600;
    box-shadow: 0 3px 10px rgba(184,95,46,0.4);
  }
  .chip.ghost { background: transparent; border: 1px dashed var(--chip-border); color: var(--text-muted); }
  #genreChips {
    flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-inline: contain;
    scroll-snap-type: inline proximity; padding: 2px 2px 10px;
    scrollbar-width: thin;
  }
  .pt-genre-art-chip {
    flex: 0 0 116px; min-width: 116px; padding: 0; border-radius: 14px;
    overflow: hidden; text-align: left; scroll-snap-align: start;
  }
  .pt-genre-art-image {
    display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
    border-bottom: 1px solid var(--chip-border);
  }
  .pt-genre-art-chip span {
    display: block; min-height: 44px; padding: 8px 9px;
    line-height: 1.25; text-align: center;
  }
  .pt-genre-art-chip.active .pt-genre-art-image { border-bottom-color: rgba(255,255,255,0.28); }
  #genreChips > .chip.ghost { flex: 0 0 auto; align-self: center; }

  .instrument-grid { display: flex; flex-wrap: wrap; gap: 9px; margin-bottom: 10px; }
  .instrument-grid label {
    display: inline-flex; align-items: center; gap: 0; margin: 0; font-size: 0;
  }
  .instrument-grid input[type=checkbox] { display: none; }
  .instrument-chip {
    font-size: 12.5px; padding: 6px 12px; border-radius: 100px;
    background: var(--chip-bg); border: 1px solid var(--chip-border);
    color: rgba(246,239,226,0.8);
  }
  .instrument-grid input[type=checkbox]:checked + .instrument-chip {
    background: linear-gradient(135deg, var(--copper), var(--copper-dark));
    border-color: transparent; color: #fff; font-weight: 600;
    box-shadow: 0 3px 10px rgba(184,95,46,0.4);
  }

  /* ---------- sliders ---------- */
  .finetune-row { display: flex; flex-direction: column; gap: 18px; }
  .finetune-control label { display: flex; justify-content: space-between; margin-bottom: 7px; }
  .finetune-value { color: var(--copper); font-weight: 700; text-transform: none; }
  input[type=range] { width: 100%; accent-color: var(--copper); margin-top: 2px; }

  /* Indeterminate progress bar - no real % available (the backend doesn't
     report incremental progress during Demucs processing), so this is an
     honest "still working" signal, not a fake percentage. */
  .progress-track {
    width: 100%; height: 6px; border-radius: 100px; overflow: hidden;
    background: rgba(255,255,255,0.08); margin-top: 8px;
  }
  .progress-bar {
    width: 40%; height: 100%; border-radius: 100px;
    background: linear-gradient(90deg, var(--copper), var(--copper-dark));
    animation: progress-slide 1.4s ease-in-out infinite;
  }
  @keyframes progress-slide {
    0% { margin-left: -40%; }
    100% { margin-left: 100%; }
  }
  @media (prefers-reduced-motion: reduce) {
    .progress-bar { animation: none; margin-left: 0; width: 100%; opacity: 0.5; }
  }

  .coming-soon {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 9px 12px; border-radius: 12px; margin-top: 10px;
    background: rgba(255,255,255,0.04); border: 1px dashed var(--chip-border);
    font-size: 11.5px; color: var(--text-muted);
  }
  .coming-soon b { color: rgba(246,239,226,0.85); font-weight: 600; }
  .coming-soon .badge {
    font-size: 9.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    background: rgba(255,255,255,0.1); padding: 3px 7px; border-radius: 100px; flex-shrink: 0;
  }

  /* Prompt Only mode notice (PT-20260819-004) - deliberately NOT the
     .coming-soon dashed/muted treatment, which reads as "unfinished."
     This is a solid copper-accented box so the simplified mode reads as
     an intentional design choice, not a degraded one. */
  .prompt-only-notice {
    margin-bottom: 14px; padding: 12px 14px; border-radius: 12px;
    background: rgba(224,137,79,0.08); border: 1px solid rgba(224,137,79,0.35);
  }
  .prompt-only-notice strong { display: block; color: var(--copper); font-size: 13px; margin-bottom: 4px; }
  .prompt-only-notice p { margin: 4px 0 0; font-size: 12.5px; line-height: 1.5; color: rgba(246,239,226,0.85); }
  .prompt-only-notice .prompt-only-note { color: var(--text-muted); font-size: 11.5px; margin-top: 6px; }

  .server-row { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-muted); padding: 2px 4px; }

  /* ---------- status indicator (unchanged semantics) ---------- */
  .status-indicator { font-size: 11px; font-weight: 700; margin-left: 4px; }
  .status-indicator.ok { color: #4fd1a5; }
  .status-indicator.ok::after { content: "\2713 connected"; }
  .status-indicator.ok-account::after { content: "\2713 signed in"; }
  .status-indicator.ok-key::after { content: "\2713 license key valid"; }
  .status-indicator.ok-account, .status-indicator.ok-key { color: #4fd1a5; }
  .status-indicator.bad { color: #ff9d8a; }
  .status-indicator.bad::after { content: "\2717 " attr(data-reason); }
  .status-indicator.checking { color: var(--text-muted); }
  .status-indicator.checking::after { content: "checking..."; }

  /* ---------- account modal (unchanged logic, restyled surface) ---------- */
  .modal-backdrop {
    display: none; position: fixed; inset: 0;
    background: rgba(10, 11, 17, 0.6); z-index: 40;
    align-items: flex-start; justify-content: center; padding: 70px 20px;
  }
  .modal-backdrop.open { display: flex; }
  /* PT-LIBRARY-LAB-20260911: confirmModal is the app's one generic
     "confirm this" overlay (showConfirmModal), reused by libraryModal's
     own delete confirmation - it must always render above any OTHER
     open modal it was triggered from, not just above the base page,
     or a confirm triggered while libraryModal is open would render
     underneath it and be unreachable. Explicit higher z-index rather
     than relying on DOM order, which is fragile against future modals
     added anywhere in the document. */
  #confirmModal { z-index: 50; }
  .modal-panel {
    background: #1c2032; border: 1px solid var(--glass-border);
    border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,0.5);
    width: 100%; max-width: 380px; max-height: 80vh; overflow-y: auto;
    padding: 20px 22px; color: var(--text);
  }
  .modal-panel .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
  .modal-panel .modal-header h2 { margin: 0; font-size: 16px; }
  .modal-close { background: none; border: none; font-size: 18px; line-height: 1; color: var(--text-muted); cursor: pointer; padding: 4px; }
  .modal-panel .card { padding: 14px 0 0; }
  .modal-panel hr { border: none; border-top: 1px solid var(--glass-border); margin: 16px 0; }
  details summary { cursor: pointer; font-size: 12px; color: var(--text-muted); }

  /* ---------- My Library: a full-screen app section, never a modal
     (PT-LIBRARY-20260911-LIBRARY2) - z-index 45, below #confirmModal's
     50 so the delete-confirmation dialog still renders above it, above
     the base page's own content. Dimensions are fixed/100vh from the
     start (no content-driven resize while loading - the real owner
     complaint this replaces), matching an app screen, not a shrink-
     wrapped dialog. ---------- */
  .pt-library-screen {
    display: none; position: fixed; inset: 0; z-index: 45;
    background: var(--bg, #0a0b11); flex-direction: column;
    width: 100%; max-width: 100%; min-width: 0; height:100vh; height:100dvh;
    overflow: hidden; padding-bottom:env(safe-area-inset-bottom, 0px);
  }
  .pt-library-screen.open { display: flex; }
  .pt-library-header {
    display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center;
    padding: 16px 20px; border-bottom: 1px solid var(--glass-border);
    flex: 0 0 auto; width: 100%; max-width: 100%; min-width: 0;
  }
  .pt-library-header h2 { margin: 0; min-width: 0; font-size: 18px; overflow-wrap: anywhere; }
  .pt-library-tabs { display:flex; justify-content:center; gap:6px; min-width:0; max-width:100%; }
  .pt-library-tabs button { min-width:0; min-height:40px; padding:8px 14px; border:1px solid rgba(255,255,255,.22); border-radius:999px; background:transparent; color:var(--pt-ink-soft); font-weight:700; white-space:normal; overflow-wrap:anywhere; }
  .pt-library-tabs button[aria-selected="true"] { background:var(--pt-accent); color:#17202a; border-color:var(--pt-accent); }
  .pt-library-close { justify-self:end; background: none; border: none; font-size: 22px; line-height: 1; color: var(--text-muted); cursor: pointer; padding: 4px 8px; }
  .pt-library-body {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    width:100%; max-width:100%; min-width:0; overflow: hidden;
  }
  .pt-library-body[hidden] { display: none !important; }
  .pt-library-master-detail { flex:1 1 auto; min-height:0; min-width:0; width:100%; max-width:100%; overflow:hidden; }
  .pt-library-master-detail[hidden] { display:none !important; }
  .pt-library-share-panel { margin:10px 0 14px; }
  .pt-library-share-panel .ptsp-panel { max-width:none; }
  .pt-library-share-panel[hidden], .pt-library-primary-actions button[hidden] { display:none !important; }
  .pt-library-make-video, .pt-library-new-video {
    display:block; width:100%; min-height:50px; margin:10px 0 12px;
    font-weight:850; letter-spacing:.01em;
    background:linear-gradient(100deg,#f36f21 0%,#ffbd48 52%,#8faed4 100%);
    color:#101827; border:0; box-shadow:0 10px 28px rgba(243,111,33,.24);
  }
  .pt-library-make-video[hidden], .pt-library-new-video[hidden] { display:none !important; }
  .pt-library-group-optin { margin:18px 0 0; padding:12px; display:flex; align-items:center; justify-content:space-between; gap:10px; border:1px solid rgba(255,189,72,.42); border-radius:14px; background:rgba(255,189,72,.08); }
  .pt-library-group-optin div { min-width:0; display:grid; gap:3px; }
  .pt-library-group-optin strong { color:var(--pt-ink); }
  .pt-library-group-optin span { color:var(--pt-ink-soft); font-size:12px; line-height:1.4; }
  .pt-library-group-optin a { flex:0 0 auto; text-decoration:none; text-align:center; }
  .pt-library-engagement { margin:8px 0 10px; display:flex; align-items:center; flex-wrap:wrap; gap:7px; color:var(--pt-ink-soft); font-size:12px; }
  .pt-library-engagement button { min-width:38px; min-height:38px; padding:5px 10px; font-size:17px; }
  .pt-library-engagement button[aria-pressed="true"] { border-color:var(--pt-accent); background:rgba(255,189,72,.18); }
  .pt-library-rating-status { flex-basis:100%; min-height:1em; color:var(--pt-accent); }
  .pt-library-rating-status:empty { display:none; }
  .pt-library-primary-actions { margin-top:0; gap:7px; display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); }
  .pt-library-primary-actions button { width:100%; min-width:0; min-height:40px; padding:7px 12px; font-size:14px; }
  .pt-library-cover-chooser { margin:10px 0 12px; padding:14px; border:1px solid rgba(255,189,72,.36); border-radius:14px; background:rgba(255,189,72,.06); }
  .pt-library-cover-chooser[hidden] { display:none !important; }
  .pt-library-cover-choice-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:12px; }
  .pt-library-cover-choice-grid label { min-width:0; display:grid; gap:7px; color:var(--pt-ink-soft); font-size:13px; font-weight:700; }
  .pt-library-cover-choice-grid select,
  .pt-library-cover-choice-grid input { width:100%; min-width:0; min-height:42px; margin:0; padding:8px 10px; border:1px solid var(--pt-line); border-radius:10px; background:var(--pt-surface); color:var(--pt-ink); }
  .pt-library-cover-requirements { margin:10px 0 0; color:var(--pt-ink-soft); font-size:12px; line-height:1.45; }
  .pt-library-cover-status { min-height:1.2em; margin:7px 0 0; color:var(--pt-accent); font-size:12px; font-weight:700; }
  .pt-library-cover-status:empty { display:none; }
  .pt-library-lyrics-tools { margin-top:8px; display:grid; grid-template-columns:minmax(220px,420px) auto; align-items:center; gap:12px; }
  .pt-library-lyrics-tools .pt-library-language { width:100%; margin:0; min-height:40px; padding:7px 12px; }
  .pt-library-lyrics-tools .pt-library-timings { margin:0; }
  .pt-library-video-list { display:grid; gap:8px; width:100%; }
  .pt-library-video-card { width:100%; min-width:0; padding:12px; text-align:left; border:1px solid rgba(255,255,255,.18); border-radius:14px; background:rgba(255,255,255,.06); color:var(--pt-ink); }
  .pt-library-video-card h3 { margin:0 0 5px; color:var(--pt-ink); font-size:16px; overflow-wrap:anywhere; }
  .pt-library-video-card p { margin:0 0 10px; color:var(--pt-ink-soft); font-size:12px; }
  .pt-library-video-detail video { display:block; width:min(100%,720px); max-height:62vh; margin:12px auto; border-radius:14px; background:#000; }
  .pt-library-video-actions { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; }
  .pt-library-video-actions button { width:100%; min-width:0; }
  .pt-library-video-rating { display:flex; align-items:center; gap:8px; margin-top:10px; }
  .pt-library-video-rating button { min-width:44px; min-height:44px; padding:7px 10px; font-size:18px; }
  .pt-library-video-rating button[aria-pressed="true"] { border-color:var(--pt-accent); background:rgba(255,189,72,.18); }
  .pt-library-video-note { max-width:900px; margin:0 auto 14px; color:var(--pt-ink-soft); font-size:12px; line-height:1.5; }
  .pt-library-ideas-view { flex:1 1 auto; min-height:0; min-width:0; width:100%; max-width:100%; }
  .pt-library-ideas-view[hidden], .pt-library-idea-editor[hidden] { display:none !important; }
  .pt-library-ideas-view:not([hidden]) { display:flex; }
  #libraryIdeasList { display:grid; gap:10px; min-width:0; width:100%; }
  .pt-library-idea-card { width:100%; text-align:left; padding:16px; border:1px solid rgba(255,255,255,.18); border-radius:16px; background:rgba(255,255,255,.06); color:var(--pt-ink); }
  .pt-library-idea-card strong, .pt-library-idea-card span { display:block; }
  .pt-library-idea-card strong, .pt-library-idea-card span { max-width:100%; overflow-wrap:anywhere; word-break:break-word; }
  .pt-library-idea-card span { margin-top:5px; color:var(--pt-ink-soft); }
  .pt-library-idea-editor { width:100%; min-width:0; display:grid; gap:14px; }
  .pt-library-filters { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:6px; margin-bottom:14px; }
  .pt-library-filters button { min-width:0; min-height:38px; padding:7px 6px; border-radius:999px; background:transparent; border:1px solid rgba(255,255,255,.18); color:var(--pt-ink-soft); font-size:12px; font-weight:750; }
  .pt-library-filters button[aria-selected="true"] { color:#17202a; background:var(--pt-accent); border-color:var(--pt-accent); }
  .pt-library-filters button span { opacity:.76; }
  .pt-library-idea-editor label { display:grid; gap:6px; color:var(--pt-ink-soft); font-weight:700; }
  .pt-library-idea-editor input, .pt-library-idea-editor textarea { display:block; width:100%; min-width:0; max-width:100%; padding:12px; border:1px solid rgba(255,255,255,.2); border-radius:12px; background:var(--pt-surface); color:var(--pt-ink); }
  .pt-library-idea-actions { display:flex; flex-wrap:wrap; gap:8px; }
  .pt-library-list-pane, .pt-library-detail-pane {
    flex: 1 1 auto; min-width:0; max-width:100%; min-height: 0; overflow-y: auto; overflow-x:hidden; padding: 16px 20px 32px;
    -webkit-overflow-scrolling: touch; touch-action: pan-y;
  }
  .pt-library-status { font-size: 13px; color: var(--text-muted); min-height: 16px; overflow-wrap:anywhere; word-break:break-word; }
  .pt-library-empty { font-size: 13px; color: var(--text-muted); text-align: center; margin-top: 40px; }
  .pt-library-meta { font-size: 12px; color: var(--text-muted); margin: 0 0 14px; }
  .pt-library-song-row {
    display: flex; width: 100%; text-align: left; margin-top: 8px;
    padding: 8px; gap: 10px; align-items: center; min-height: 74px;
  }
  .pt-library-cover-thumb {
    width: 56px; height: 56px; flex: 0 0 56px; object-fit: cover;
    border-radius: 9px; border: 1px solid rgba(255,255,255,0.16);
    background: rgba(255,255,255,0.04);
  }
  .pt-library-song-row-copy { min-width: 0; max-width:100%; display: flex; flex-direction: column; gap: 5px; overflow:hidden; }
  .pt-library-song-row-copy strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pt-library-song-row-meta { max-width:100%; font-size: 11px; color: var(--text-muted); overflow-wrap:anywhere; word-break:break-word; }
  .pt-library-cover-detail {
    width: min(100%, 430px); margin: 0 auto 18px; position: relative;
  }
  .pt-library-cover-art {
    display: block; width: 100%; height: auto; aspect-ratio: 1 / 1;
    object-fit: cover; border-radius: 18px;
    border: 1px solid rgba(255,255,255,0.16);
    box-shadow: 0 18px 55px rgba(0,0,0,0.34);
    background: rgba(255,255,255,0.04);
  }
  .pt-library-cover-detail figcaption {
    position: absolute; left: 10px; bottom: 10px; padding: 5px 9px;
    border-radius: 999px; background: rgba(5,8,13,0.72); color: var(--text);
    font-size: 11px; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  }
  .pt-library-section { margin-bottom: 16px; }
  .pt-library-section h3 { font-size: 13px; margin: 0 0 4px; color: var(--text); }
  .pt-library-text { max-width:100%; white-space: pre-wrap; overflow-wrap:anywhere; word-break:break-word; font-size: 13px; margin: 0; color: var(--text); }
  .pt-library-detail-placeholder { font-size: 13px; color: var(--text-muted); text-align: center; margin-top: 60px; }
  .pt-library-title-row { display:flex; gap:8px; align-items:center; min-width:0; max-width:100%; margin-bottom:14px; }
  .pt-library-title-row input { min-width:0; max-width:100%; }
  .pt-library-meta, .pt-library-cover-detail figcaption, .pt-library-timings { overflow-wrap:anywhere; word-break:break-word; }

  /* Stable-height Song-row skeleton - reserves the same footprint a
     real row takes, so the list never visibly resizes/jumps once real
     rows replace it. */
  .pt-library-row-skeleton {
    height: 56px; border-radius: 10px; margin-top: 8px;
    background: linear-gradient(90deg, rgba(255,255,255,0.04) 25%, rgba(255,255,255,0.08) 37%, rgba(255,255,255,0.04) 63%);
    background-size: 400% 100%; animation: pt-library-skeleton-shimmer 1.4s ease infinite;
  }
  @keyframes pt-library-skeleton-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

  /* Mobile default (matches the rest of this file's mobile-first
     baseline): exactly one pane visible at a time, list first. */
  .pt-library-detail-pane { display: none; }
  .pt-library-body.pt-library-show-detail .pt-library-list-pane { display: none; }
  .pt-library-body.pt-library-show-detail .pt-library-detail-pane { display: block; }
  .pt-library-master-detail.pt-library-show-detail > .pt-library-list-pane { display:none; }
  .pt-library-master-detail.pt-library-show-detail > .pt-library-detail-pane { display:block; }
  @media (max-width: 859px) {
    /* iOS needs the active Videos/Ideas section to be a bounded flex
       container.  As a plain block, its detail child expands to content
       height and the parent's overflow:hidden clips it, leaving Safari no
       scrollable box to move even though the panel looks correct.  Apply the
       same contract to every Library section and both of its panes. */
    .pt-library-master-detail:not([hidden]) {
      display:flex; flex-direction:column; height:100%; max-height:100%; min-height:0;
    }
    .pt-library-master-detail > .pt-library-list-pane,
    .pt-library-master-detail > .pt-library-detail-pane {
      flex:1 1 auto; height:auto; max-height:100%; min-height:0;
    }
    .pt-library-ideas-view:not([hidden]) { flex-direction:column; }
  }
  .pt-library-back { display: inline-block; margin-bottom: 10px; }

  /* Desktop: stable two-column layout, list and detail both always
     visible - "show detail" only decides which pane a returning-to-
     mobile-width session would see, it never hides the list pane here. */
  @media (min-width: 860px) {
    .pt-library-body { flex-direction: row; }
    .pt-library-master-detail:not([hidden]) { display:flex; flex-direction:row; }
    .pt-library-list-pane {
      flex: 0 0 340px; border-right: 1px solid var(--glass-border);
    }
    .pt-library-detail-pane, .pt-library-body.pt-library-show-detail .pt-library-detail-pane {
      display: block; flex: 1 1 auto;
    }
    .pt-library-master-detail > .pt-library-list-pane,
    .pt-library-master-detail.pt-library-show-detail > .pt-library-list-pane { display:block; flex:0 0 340px; }
    .pt-library-master-detail > .pt-library-detail-pane,
    .pt-library-master-detail.pt-library-show-detail > .pt-library-detail-pane { display:block; flex:1 1 auto; }
    .pt-library-body.pt-library-show-detail .pt-library-list-pane,
    .pt-library-list-pane { display: block; }
    .pt-library-back { display: none; }
    .pt-library-cover-thumb { width: 64px; height: 64px; flex-basis: 64px; }
    .pt-library-song-row { min-height: 82px; }
    .pt-library-ideas-view.pt-library-ideas-show-detail { overflow:hidden; }
    .pt-library-ideas-view.pt-library-ideas-show-detail .pt-library-ideas-list,
    .pt-library-ideas-view.pt-library-ideas-show-detail .pt-library-idea-editor {
      min-height: 0; overflow-y: auto; max-width: none; margin: 0;
    }
    .pt-library-ideas-view.pt-library-ideas-show-detail .pt-library-ideas-list {
      padding-right: 16px; border-right: 1px solid var(--glass-border);
    }
  }

  @media (max-width: 859px) {
    .pt-library-header { grid-template-columns: 1fr auto; row-gap:10px; }
    .pt-library-tabs { grid-column:1 / -1; grid-row:2; width:100%; }
    .pt-library-tabs button { flex:1 1 0; }
    .pt-library-close { grid-column:2; grid-row:1; }
    .pt-library-ideas-view.pt-library-ideas-show-detail .pt-library-ideas-list { display:none; }
    /* Keep the selected media and its real controls together in the first
       phone viewport. Detail panes still scroll normally, but Android and
       iPhone no longer present a full screen of artwork/video with Share,
       Download, and Delete stranded below the browser chrome. */
    .pt-library-detail-pane { overscroll-behavior:contain; padding-bottom:calc(40px + env(safe-area-inset-bottom, 0px)); }
    .pt-library-cover-detail { width:min(68vw, 260px); }
    .pt-library-video-detail video { width:min(100%, 560px); max-height:44vh; max-height:min(44dvh, 420px); object-fit:contain; }
    /* Controls follow the player in normal document flow.  A sticky action
       stack covered tall/portrait videos on Samsung devices, leaving only
       the native scrubber visible behind Share/Download/Delete. */
    .pt-library-video-actions {
      position:static; z-index:auto; grid-template-columns:repeat(3,minmax(0,1fr));
      padding:4px 0 8px; background:none;
    }
    .pt-library-video-actions button { min-height:44px; padding:8px 6px; font-size:14px; }
  }

  @media (max-width: 480px) {
    .pt-library-header { padding:12px 14px; }
    .pt-library-header h2 { font-size:16px; }
    .pt-library-tabs button { padding:8px 9px; font-size:12px; }
    .pt-library-list-pane, .pt-library-detail-pane { padding-left: 14px; padding-right: 14px; }
    .pt-library-ideas-view { padding:14px; }
    .pt-library-videos-view { padding:14px; }
    .pt-library-video-actions { grid-template-columns:repeat(3,minmax(0,1fr)); }
    .pt-library-group-optin { align-items:stretch; flex-direction:column; }
    .pt-library-group-optin a { width:100%; }
    .pt-library-cover-detail { width:min(52vw, 200px); margin-bottom:14px; }
    .pt-library-title-row { align-items:stretch; flex-direction:column; }
    .pt-library-title-row > button { width:100%; }
    #libraryDetailContent > .row { flex-direction: column; }
    #libraryDetailContent > .row > button { width: 100%; flex: 0 0 auto !important; }
    .pt-library-primary-actions,
    .pt-library-cover-choice-grid { grid-template-columns:1fr; }
    .pt-library-idea-actions { display:grid; grid-template-columns:1fr; }
    .pt-library-idea-actions button { width:100%; min-width:0; white-space:normal; }
    .pt-library-idea-editor input,
    .pt-library-idea-editor textarea,
    .pt-library-language { font-size:16px; }
  }

  /* ---------- tab bar: lives inside #billingCard, flush to its
     bottom at every screen size (not a separate floating rail) ---------- */
  #billingCard {
    display: flex; flex-direction: column;
  }
  .tab-bar {
    display: flex; gap: 4px; justify-content: center;
    background: rgba(12,13,20,0.65); border: 1px solid rgba(255,255,255,0.12);
    backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
    border-radius: 100px; padding: 6px;
    /* iPhone home-indicator safe area (item 10) - the bar itself is
       still visible without this, but its bottom edge sat flush against
       the indicator with no breathing room. */
    margin: 0 0 max(0px, env(safe-area-inset-bottom)) 0;
    margin-top: auto;
  }
  .tab {
    width: 48px; height: 48px; border-radius: 50%; padding: 0;
    display: flex; align-items: center; justify-content: center;
    background: transparent; color: rgba(246,239,226,0.55);
  }
  .tab.active { background: linear-gradient(135deg, var(--copper), var(--copper-dark)); color: #fff; }
  .tab:disabled { color: rgba(246,239,226,0.28); cursor: not-allowed; }
  .tab svg { width: 20px; height: 20px; }
  .tab#accountBtn { position: relative; }
  .tab#accountBtn::after {
    content: ""; position: absolute; top: 6px; right: 6px;
    width: 9px; height: 9px; border-radius: 50%;
    background: #6b7085; border: 2px solid #0c0d14;
  }
  .tab#accountBtn.connected::after { background: #4fd1a5; }

  @media (min-width: 760px) {
    .page { padding-top: 40px; }
  }

  /* ---------- desktop: left rail + 2-col grid + sticky mascot,
     matching the approved Porch Glass mockup's desktop treatment ---------- */
  @media (min-width: 900px) {
    .page { padding-left: 96px; }
    .panel {
      max-width: 1380px;
      display: grid;
      grid-template-columns: 340px minmax(0, 1fr) minmax(0, 1fr) 340px;
      gap: 18px;
    }
    .title-block { grid-area: title; padding-top: 0; }
    .pill-row { grid-area: pills; justify-content: center; }
    .hero-card { grid-area: hero; position: sticky; top: 24px; align-self: start; }
    .g-billing { grid-area: billing; align-self: start; position: sticky; top: 24px; }
    .g-genre { grid-area: genre; }
    .g-mood { grid-area: mood; }
    .g-instruments { grid-area: instr; }
    .g-vocals { grid-area: vocals; }
    .g-production { grid-area: prod; }
    .g-lyrics { grid-area: lyrics; }
    .g-details { grid-area: details; }
    .g-model { grid-area: model; }
    .panel {
      grid-template-areas:
        "title   title   title   title"
        "pills   pills   pills   pills"
        "billing model   model   hero"
        "billing lyrics  details hero"
        "billing genre   mood    hero"
        "billing instr   instr   hero"
        "billing vocals  vocals  hero"
        "billing prod    prod    hero";
    }

  }

/* ============================================================================
   PORCHTUNES DESIGN SYSTEM — Phase 4 approved foundation (Batch 2)
   Layered after the original rules above. Establishes tokens, the brand
   shell, focus, safe-area, reduced-motion and responsive foundations only.
   Arrival/Create controls and generation/result states are rebuilt later.
   ========================================================================= */

:root {
  /* --- surfaces: dark slate foundation --- */
  --pt-bg: #0f1418;
  --pt-surface: #19212a;
  --pt-surface-2: #212b36;
  --pt-line: rgba(255, 255, 255, 0.11);

  /* --- text --- */
  --pt-ink: #f3f6f8;
  --pt-ink-soft: #b6c3cd;
  --pt-ink-faint: #8d9ba6;
  --pt-placeholder: #a2b0bc;

  /* --- accent: warm companion + approved Sunset -> Dusk CTA gradient --- */
  --pt-accent: #f0ab4e;
  --pt-accent-strong: #f8c47a;
  --pt-cta-ink: #111827;
  --pt-cta-gradient: linear-gradient(102deg,
    #de5c2f 0%, #e8722f 9%, #ef8834 17%, #f2953a 24%, #f5a541 32%,
    #f8b44a 41%, #fabd53 48%, #fac05a 54%, #f0b25f 61%, #e0a76a 68%,
    #cba382 76%, #ae9f97 83%, #95a0b1 90%, #7f97b6 96%, #7593b8 100%);
  --pt-dusk: #5e7ea6;
  --pt-glow: #fac05a;

  /* --- typography --- */
  --pt-serif: "Fraunces", Georgia, "Times New Roman", serif;
  --pt-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;

  /* --- spacing / radius / depth --- */
  --pt-gap-xs: 6px;  --pt-gap-sm: 10px; --pt-gap: 16px;
  --pt-gap-lg: 24px; --pt-gap-xl: 36px;
  --pt-radius-sm: 12px; --pt-radius: 20px; --pt-radius-lg: 28px;
  --pt-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
  --pt-shadow-soft: 0 8px 22px rgba(0, 0, 0, 0.32);

  /* --- interaction --- */
  --pt-tap: 44px;
  --pt-focus: #f8c47a;

  /* --- safe areas --- */
  --pt-safe-t: env(safe-area-inset-top, 0px);
  --pt-safe-r: env(safe-area-inset-right, 0px);
  --pt-safe-b: env(safe-area-inset-bottom, 0px);
  --pt-safe-l: env(safe-area-inset-left, 0px);

  --pt-hero: url("hero-sunset.jpg");
}

/* ---------- focus: visible on every interactive element ---------- */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--pt-focus);
  outline-offset: 3px;
  border-radius: 6px;
}

/* ---------- brand shell ---------- */
.pt-shell {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-left: var(--pt-safe-l);
  padding-right: var(--pt-safe-r);
}

.pt-hero {
  position: relative;
  overflow: hidden;
  height: 190px;
  background-image: var(--pt-hero);
  background-size: cover;
  background-position: center 56%;
}
.pt-hero::before,
.pt-hero::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.pt-hero::before {
  background:
    linear-gradient(180deg, rgba(8, 14, 20, 0.66) 0%, rgba(8, 14, 20, 0.34) 20%,
      rgba(8, 14, 20, 0.06) 42%, rgba(8, 14, 20, 0) 58%),
    radial-gradient(78% 60% at 50% 96%, rgba(232, 176, 106, 0.26) 0%, rgba(232, 176, 106, 0) 70%);
}
.pt-hero::after {
  background: linear-gradient(180deg, rgba(20, 23, 28, 0) 52%, var(--pt-bg) 100%);
}
.pt-hero-bar {
  position: absolute;
  inset: var(--pt-safe-t) var(--pt-safe-r) auto var(--pt-safe-l);
  z-index: 3;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 16px 22px 0;
}
.pt-hero-bar > .pt-hero-slot-end { justify-self: end; }
/* Martin Services logo - single canonical asset/reference. Replace the
   file at web/assets/branding/martin-services-logo.png (same filename)
   to swap the mark everywhere at once; this is its only <img> tag on the
   page. Secondary to the PorchTunes wordmark: grouped and centered as
   one pair (not the hero's own start slot, which stays empty - avoids a
   second, duplicate placement), sized to ~60-70% of the wordmark's own
   rendered height (~49-53px) rather than a redesign of either mark.
   object-fit keeps future variants (e.g. only the lower symbol changing)
   crisp at any size without new CSS. */
.pt-brand-group { justify-self: center; display: flex; align-items: center; gap: 10px; }
/* Secondary to the PorchTunes wordmark (~49-53px rendered height) but
   genuinely recognizable on desktop - roughly wordmark-height, not the
   ~28-32px treatment that read as an afterthought. PorchTunes stays
   visually primary via the wordmark's own weight plus the tagline
   directly underneath it. */
.pt-brand-logo { width: 40px; height: 40px; object-fit: contain; display: block; flex: none; }
@media (min-width: 768px) {
  .pt-brand-logo { width: 48px; height: 48px; }
}

/* ---------- W2 stacked wordmark (live text) ---------- */
.pt-wordmark {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  justify-self: center;
  color: #ffffff;
  text-shadow: 0 2px 12px rgba(5, 12, 18, 0.78);
}
.pt-wordmark-line {
  font-family: var(--pt-serif);
  font-weight: 600;
  font-optical-sizing: auto;
  font-size: 19px;
  line-height: 1;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
}
.pt-wordmark-line.is-lower { opacity: 0.92; }
.pt-wordmark-rule {
  width: 42px;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
}

/* PT-LAB-20260830-021: the Living Sound Bloom (.pt-bloom, .pt-bloom-core,
   .pt-bloom-glyph and, further below, .pt-gen-bloom, .pt-gen-face, the
   .pt-gen-eye and .pt-gen-arm variants, .pt-gen-mouth, and their
   ptBloomBreathe and ptBloomAlive keyframes) removed entirely - real
   owner report ("the glowing orb remains beside the arrival headline"),
   superseded by the official Porch Buddy artwork. Comprehensive removal:
   markup (index.html), the brand.js function that generated it, and
   every CSS rule, animation, keyframe, and reduced-motion override below
   - not just the one instance reported, so nothing is left that could
   resurface on a screen this audit didn't happen to check.

   PT-LAB-20260830-024 note: an earlier version of this exact comment
   accidentally embedded the two-character CSS comment-close token
   (an asterisk immediately followed by a forward slash) mid sentence,
   used as shorthand while documenting removed selector name variants -
   that closed THIS comment early and dumped the rest of the explanation
   into the stylesheet as real (garbage) CSS. Caught by running an
   actual CSS parser (css-tree) against this file, not just a raw brace
   count. Every selector-name variant is spelled out in full above
   instead, and this note itself deliberately describes the token
   instead of typing it, so it can't repeat the same mistake.

   ---------- fireflies (separable layer, never baked into the photo) ---------- */
.pt-fireflies {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
}
.pt-ff {
  position: absolute;
  width: var(--w, 3px);
  height: var(--w, 3px);
  border-radius: 50%;
  opacity: 0;
  background: radial-gradient(circle, rgba(255, 247, 214, 0.95) 0%,
    rgba(255, 226, 146, 0.6) 42%, rgba(255, 214, 110, 0) 74%);
  filter: drop-shadow(0 0 4px rgba(255, 228, 152, 0.72));
  animation: ptFfBlink var(--d, 5s) ease-in-out infinite;
  animation-delay: var(--dl, 0s);
}
.pt-ff.is-drift {
  animation: ptFfBlink var(--d, 5s) ease-in-out infinite,
             ptFfDrift var(--dd, 26s) ease-in-out infinite alternate;
}
@keyframes ptFfBlink {
  0%, 100% { opacity: 0 }   9% { opacity: .12 } 21% { opacity: .82 }
  33% { opacity: .28 }      47% { opacity: .9 } 61% { opacity: .18 }
  76% { opacity: .55 }      89% { opacity: .05 }
}
@keyframes ptFfDrift {
  from { transform: translate(0, 0) }
  to   { transform: translate(var(--dx, 0), var(--dy, 0)) }
}

/* ---------- primary CTA token (applied to controls in Batch 3) ---------- */
.pt-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--pt-tap);
  padding: 15px 26px;
  border: 0;
  border-radius: 100px;
  font: 700 16px/1 var(--pt-sans);
  color: var(--pt-cta-ink);
  background: var(--pt-cta-gradient);
  box-shadow: 0 10px 24px rgba(120, 80, 40, 0.38);
  cursor: pointer;
}

/* ---------- responsive foundation ---------- */
/* tablet: an intentional intermediate tier, previously missing */
@media (min-width: 700px) {
  .pt-hero { height: 230px; }
  .pt-wordmark-line { font-size: 20px; }
  .pt-wordmark-rule { width: 48px; }
  .pt-hero-bar { padding: 20px 32px 0; }
}
@media (min-width: 1100px) {
  .pt-hero { height: 260px; background-position: center 46%; }
  .pt-wordmark-line { font-size: 21px; }
  .pt-wordmark-rule { width: 54px; }
  .pt-hero-bar { padding: 22px 40px 0; }
}

/* no element may push the page sideways */
html, body { max-width: 100%; overflow-x: hidden; }

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .pt-ff { animation: none; opacity: 0.38; }
  .pt-ff:nth-child(n + 4) { display: none; }
}

/* Persistent brand mark (every screen except arrival - see the
   body[data-screen="arrival"] .pt-logo-hero rule below). Same rough
   position/overlap as before: a normal block-level sibling of
   .pt-shell (not position:absolute), so it can only ever render after
   .pt-shell's own layout box ends. PT-LAB-20260830-024: added a real
   max-width in ADDITION to the width:clamp() rule (defense in depth -
   never one rule standing alone between this and an oversized render)
   and reduced the <img>'s own intrinsic width/height attributes
   (index.html) from the artwork's real 1254x1254 native pixels down to
   200x200, so a worst-case total-CSS failure still falls back small. */
.pt-logo-hero { display: flex; justify-content: center; margin-top: -45px; position: relative; z-index: 2; }
.pt-logo-hero-img {
  width: clamp(150px, 30vw, 236px);
  max-width: 236px;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  display: block;
}
@media (min-width: 700px) {
  .pt-logo-hero { margin-top: -71px; }
}
@media (min-width: 1100px) {
  .pt-logo-hero { margin-top: -60px; }
  .pt-logo-hero-img { width: 200px; max-width: 200px; }
}

/* ---------------------------------------------------------------------------
   ARRIVAL / CREATE (Batch 3)
   Presentation for re-homed existing controls. No control is duplicated and
   no id is reassigned - ui-atmosphere.js moves the live nodes in here.
   ------------------------------------------------------------------------ */

.pt-arrival {
  display: flex;
  flex-direction: column;
  gap: var(--pt-gap-lg);
  padding: var(--pt-gap-lg) 22px calc(var(--pt-gap-lg) + var(--pt-safe-b));
  padding-left: max(22px, var(--pt-safe-l));
  padding-right: max(22px, var(--pt-safe-r));
  max-width: 720px;
  margin: 0 auto;
  width: 100%;
}
/* PT-LAB-20260830-024: real owner-reported P0 - the previous design
   (.pt-logo-hero, removed) rendered the emblem as a large, detached,
   hero-overlapping brand moment living OUTSIDE .pt-arrival entirely (a
   sibling of .pt-shell, visible on every screen, not just arrival) with
   a single width:clamp() rule as its only real size constraint. On a
   real deployed revision it rendered enormous and broke the whole
   arrival layout - root cause never conclusively pinned down from
   source/pipeline inspection alone (no live-browser access was
   available to confirm), so rather than patch the old design this is a
   full owner-approved replacement: ONE emblem, scoped entirely inside
   .pt-arrival (so the existing body[data-screen]:not([data-screen=
   "arrival"]) rule below hides it on every other screen automatically -
   no separate visibility rule to keep in sync), paired directly with
   the headline/sub-copy as a single lockup component instead of
   floating alone above the page. Defense in depth against a repeat: a
   real max-width property in ADDITION to the width rule below (never
   one rule standing alone between this and an oversized render), and
   the <img>'s own intrinsic width/height attributes (index.html) are
   now a small, sane fallback size rather than the artwork's real
   1254x1254 native pixels - even a worst-case total-CSS failure now
   renders small, not viewport-breaking. */
.pt-arrival-lockup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
}
.pt-arrival-emblem {
  width: clamp(140px, 34vw, 165px);
  max-width: 165px;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  display: block;
  flex: none;
}
.pt-arrival-lockup-text { display: flex; flex-direction: column; }
.pt-headline {
  font-family: var(--pt-serif);
  font-weight: 600;
  font-size: clamp(34px, 5vw, 56px);
  line-height: 1.15;
  margin: 0;
  color: var(--pt-ink);
}
.pt-arrival-buddy-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* PT-LAB-20260903-012: the hero itself IS the single, prominent "Start
   with Porch Buddy" control - a real <button> reset to look like plain
   flex content (no default button chrome), never a floating/fixed
   element (stays in .pt-arrival-buddy-stage's own normal document
   flow, immediately below the logo/headline, so it's reachable above
   the fold without scrolling on both desktop and mobile). Hidden
   entirely once a session is no longer Inactive - see setDockState's
   own visibility toggle - so there is exactly one visible clickable
   "Start with Porch Buddy" control pre-session, never a second one
   competing with it lower on the page. */
.pt-arrival-buddy-start {
  display: flex;
  flex-direction: column;
  align-items: center;
  background: none;
  border: none;
  padding: 6px;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  border-radius: var(--pt-radius);
  transition: transform 120ms ease;
}
.pt-arrival-buddy-visual {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
}
.pt-arrival-buddy-visual::before {
  content: "";
  position: absolute;
  inset: 12% 5% 5%;
  border: 2px solid rgba(240, 171, 78, 0.72);
  border-radius: 50%;
  box-shadow: 0 0 0 0 rgba(240, 171, 78, 0.34), 0 0 28px rgba(240, 171, 78, 0.16);
  animation: pb-hero-invite 2.8s ease-in-out infinite;
}
.pt-arrival-buddy-mic {
  position: absolute;
  right: -4px;
  bottom: 10%;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  color: var(--pt-ink);
  background: var(--pt-accent);
  border: 3px solid var(--pt-bg);
  box-shadow: 0 5px 18px rgba(0, 0, 0, 0.35);
  font-size: 19px;
  line-height: 1;
  animation: pb-mic-invite 2.8s ease-in-out infinite;
}
@keyframes pb-hero-invite {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240, 171, 78, 0.34), 0 0 22px rgba(240, 171, 78, 0.12); }
  50% { box-shadow: 0 0 0 12px rgba(240, 171, 78, 0), 0 0 38px rgba(240, 171, 78, 0.28); }
}
@keyframes pb-mic-invite {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}
.pt-arrival-buddy-start:hover { transform: translateY(-1px); }
.pt-arrival-buddy-start:active { transform: translateY(0); }
.pt-arrival-buddy-start:focus-visible {
  outline: 2px solid var(--pt-accent);
  outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
  .pt-arrival-buddy-start { transition: none; }
  .pt-arrival-buddy-start:hover, .pt-arrival-buddy-start:active { transform: none; }
  .pt-arrival-buddy-visual::before, .pt-arrival-buddy-mic { animation: none; }
}

/* PT-LAB-20260904-014: BUDDY PORCH RAIL - replaces the -015 position:
   sticky dock, which real owner-reported use showed was NOT actually
   "never able to cover" anything (that claim was wrong in practice: a
   sticky element stays pinned at a fixed viewport position while
   everything else scrolls underneath it - and once Active (Mute/Stop
   shown, transcript possibly expanded) its own rendered height could
   grow well past a compact status bar, so it genuinely did end up
   sitting over Description/Lyrics/proposal actions/Quality/Make My Song
   as the page scrolled - "the oversized translucent Porch Buddy
   control"). Fixed positioning here is the deliberate, verified-safe
   opposite: pinned OUTSIDE the 720px-max .pt-arrival column entirely
   (desktop) or as a short, height-capped bar with the page's own
   content reserving space below it (mobile), rather than sticky-over.
   The compact rail itself (face + one state label + one primary mic
   control - the face button, see porch-buddy-voice.js) is always small;
   only the expanded conversation view (.pt-buddy-dock-expanded, opened
   by tapping the rail - "the existing conversation view," never a
   separate rebuilt panel) is allowed to grow, and even then it's capped
   and independently scrollable, never viewport-covering. */
.pt-buddy-dock {
  position: fixed;
  z-index: 5; /* real persistent UI, not competing with .pt-menu-sheet's 50 */
  left: auto;
  right: max(12px, var(--pt-safe-r));
  bottom: calc(var(--pt-safe-b) + 12px);
  width: min(230px, calc(100vw - 24px));
  max-width: 230px;
  margin: 0;
  max-height: 22dvh;
  overflow: visible;
  background: transparent;
  border: 0;
  border-radius: 999px;
  box-shadow: none;
  padding: 0;
  text-align: center;
  transition: max-height 200ms ease;
  /* PT-LAB-20260906-041 Part 4: drag offset ONLY - the fixed left/right/
     top/bottom rules above/below still own the dock's real anchored
     corner; this transform never replaces them, only nudges from
     wherever they already placed it. clampDockDragOffset (porch-buddy-
     voice.js) guarantees these two custom properties can never carry the
     dock outside the safe viewport. Defaults to 0px, so an untouched
     dock is pixel-identical to before this property existed. */
  transform: translate(var(--pt-buddy-drag-x, 0px), var(--pt-buddy-drag-y, 0px));
}
/* "Automatically move Buddy between the lower-left and lower-right side
   based on field/button obstruction" - toggled by dockObstructsProtected
   Area (porch-buddy-voice.js), only while genuinely overlapping a
   protected field/button, never speculatively. Never applied while
   expanded (the expanded card is already centered with its own left/
   right:12px rule, unrelated to this compact-rail-only concern). */
.pt-buddy-dock.pt-buddy-dock-left:not(.pt-buddy-dock-expanded) {
  right: auto;
  left: max(12px, var(--pt-safe-l));
}
.pt-buddy-dock.pt-buddy-dock-idle:not(.pt-buddy-hero-offscreen) {
  display: none !important;
}
.pt-buddy-dock:not(.pt-buddy-dock-expanded) .pt-buddy-dock-summary,
.pt-buddy-dock:not(.pt-buddy-dock-expanded) #pbVoiceConcludedNotice {
  display: none !important;
}
.pt-buddy-dock:not(.pt-buddy-dock-expanded) .pt-buddy-companion-face {
  width: auto;
  margin-left: auto;
  padding: 7px 10px;
  border: 1px solid rgba(240, 171, 78, 0.48);
  border-radius: 999px;
  background: rgba(20, 27, 35, 0.96);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.34);
}
/* A failure/suspend/expiry still needs a legible recovery action. These
   exceptional states get a small card; the normal idle/active companion
   remains a borderless pill rather than a permanent floating box. */
.pt-buddy-dock.pt-buddy-dock-state-suspended:not(.pt-buddy-dock-expanded),
.pt-buddy-dock.pt-buddy-dock-state-failed:not(.pt-buddy-dock-expanded),
.pt-buddy-dock.pt-buddy-dock-state-expired:not(.pt-buddy-dock-expanded) {
  padding: 8px;
  background: rgba(20, 27, 35, 0.97);
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
  box-shadow: var(--pt-shadow);
}
/* Expanded (conversation view open): allowed to grow, still capped and
   independently scrollable - "put Mute, Stop, and conversation history
   in the expanded view," never an unbounded card. */
.pt-buddy-dock.pt-buddy-dock-expanded {
  left: 12px;
  right: 12px;
  width: auto;
  max-width: 360px;
  margin: 0 auto;
  max-height: 62dvh;
  overflow-y: auto;
  background: var(--pt-surface);
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
  box-shadow: var(--pt-shadow);
  padding: 8px 10px;
}
/* PT-LAB-20260904-014: the page's own scrollable content reserves real
   space below itself equal to the collapsed rail's own footprint, so
   Make My Song (and every field/action above it) can always be
   scrolled fully clear of the fixed bar - never permanently hidden
   behind it. Sized generously (rail height + its own bottom offset +
   margin) rather than tightly, so it stays correct even if the rail's
   own content wraps to two lines on a narrow phone. */
@media (max-width: 1279.98px) {
  .pt-arrival {
    padding-bottom: calc(var(--pt-gap-lg) + var(--pt-safe-b) + 96px);
  }
}
@media (min-width: 1280px) {
  /* Desktop: docks unobtrusively at the right edge, OUTSIDE .pt-arrival's
     own 720px-max column - verified clear at this breakpoint: half the
     side gutter at 1280px is (1280-720)/2 = 280px; this rail's own
     widest (expanded) footprint is right-offset(16) + expanded-
     width(240) = 256px, comfortably inside that gutter with margin to
     spare, so it can never reach over the form itself (a dedicated test
     proves this arithmetic, not just the comment). Vertically centered
     rather than bottom-docked, since there's no on-screen keyboard or
     safe-area concern to avoid on desktop. */
  .pt-buddy-dock {
    left: auto;
    right: max(16px, calc((100vw - 720px) / 2 - 250px));
    bottom: auto;
    top: 50%;
    /* PT-LAB-20260906-041 Part 4: composes the desktop vertical-center
       with the same drag-offset custom properties the base rule uses -
       never a second, separate transform mechanism. */
    transform: translate(var(--pt-buddy-drag-x, 0px), calc(-50% + var(--pt-buddy-drag-y, 0px)));
    width: 220px;
    max-width: 220px;
    max-height: 70dvh;
  }
  .pt-buddy-dock.pt-buddy-dock-left:not(.pt-buddy-dock-expanded) {
    right: auto;
    left: max(16px, calc((100vw - 720px) / 2 - 250px));
  }
  .pt-buddy-dock.pt-buddy-dock-expanded {
    left: auto;
    right: max(16px, calc((100vw - 720px) / 2 - 250px));
    width: 240px;
    max-width: 240px;
  }
}
/* A description/lyrics field has focus. "Shrink or reposition it - do
   not make the entire control nearly invisible": the inner content
   scales down and tucks toward the rail's own anchored corner, staying
   fully opaque throughout - never a translucent/near-invisible fade.
   Also steps out of the tab order (via `inert`, toggled alongside this
   class in JS) so it's not a distracting extra stop between fields.
   Split onto a separate inner wrapper (.pt-buddy-dock-inner) so this
   transform never fights the outer element's own fixed-position/
   expanded-width transforms above. */
.pt-buddy-dock-inner { transition: transform 180ms ease; transform-origin: bottom right; }
.pt-buddy-dock.pt-buddy-dock-typing .pt-buddy-dock-inner {
  transform: scale(0.78);
}
@media (min-width: 1280px) {
  .pt-buddy-dock-inner { transform-origin: center right; }
  .pt-buddy-dock.pt-buddy-dock-typing .pt-buddy-dock-inner {
    transform: scale(0.78) translateX(10px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .pt-buddy-dock, .pt-buddy-dock.pt-buddy-dock-expanded, .pt-buddy-dock-inner {
    transition: none;
  }
}
/* PT-LAB-20260903-001, owner decision: "One clear Create with Porch
   Buddy action" - the Inactive-state dock IS that action (same element,
   just relabeled - see porch-buddy-voice.js), given modestly stronger
   visual weight than the ordinary Active/Suspended/Failed states so it
   reads as the primary way in, not merely repositioned. Once a session
   starts this class is removed (setDockState) - stays compact
   throughout, never grows into a large card. */
.pt-buddy-dock.pt-buddy-dock-primary {
  border-color: var(--pt-accent);
  box-shadow: 0 0 0 1px var(--pt-accent), var(--pt-shadow);
}
.pt-buddy-dock.pt-buddy-dock-primary .pt-buddy-companion-face {
  font-weight: 600;
}
/* PT-LAB-20260903-009: FIRST-USE ATTRACTION. Compact and mutually
   exclusive with every other dock state by construction (this class
   only ever applies alongside "inactive" - see setDockState) - a warm
   halo/pulse, a small idle voice-bar wave, and a gentle bob, all built
   on the exact ring/bars elements the speaking indicator already uses
   (PT-LAB-20260903-008), never a second/duplicate visual. Mobile keeps
   the existing compact 32px/28px sizing; only >=900px gets the larger,
   substantially-more-noticeable treatment plus the supporting subcopy
   line - "compact and non-obstructive" on mobile is the same dock,
   just without the extra scale/copy, never the old floating tile. */
.pt-buddy-dock-primary .pb-face-ring::before {
  border-color: var(--pt-accent);
  animation: pb-invite 2.6s ease-in-out infinite;
}
.pt-buddy-dock-primary .pb-face-img {
  animation: pb-bob 3.4s ease-in-out infinite;
}
.pt-buddy-dock-primary .pb-voice-bars {
  width: 20px;
}
.pt-buddy-dock-primary .pb-voice-bars span {
  animation: pb-idle-wave 1.8s ease-in-out infinite;
}
.pt-buddy-dock-primary .pb-voice-bars span:nth-child(1) { animation-delay: 0s; }
.pt-buddy-dock-primary .pb-voice-bars span:nth-child(2) { animation-delay: .15s; }
.pt-buddy-dock-primary .pb-voice-bars span:nth-child(3) { animation-delay: .3s; }
.pt-buddy-dock-primary .pb-voice-bars span:nth-child(4) { animation-delay: .45s; }
@keyframes pb-invite {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240, 171, 78, 0.32); }
  50% { box-shadow: 0 0 0 7px rgba(240, 171, 78, 0.08); }
}
@keyframes pb-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-2px); }
}
@keyframes pb-idle-wave {
  0%, 100% { height: 3px; }
  50% { height: 9px; }
}
.pt-buddy-dock-text { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
.pt-buddy-dock-subcopy {
  display: none;
  font-size: 11px;
  color: var(--pt-ink-soft);
  font-weight: 400;
  margin-top: 1px;
}
@media (min-width: 900px) {
  /* Desktop-only enlargement - the mobile/compact dock (see the
     existing >=900px width rule below) is otherwise untouched. */
  .pt-buddy-dock-primary {
    padding: 14px 16px;
  }
  .pt-buddy-dock-primary .pb-face-ring {
    width: 48px;
    height: 48px;
  }
  .pt-buddy-dock-primary .pb-face-img {
    width: 42px;
    height: 42px;
  }
  .pt-buddy-dock-primary #pbVoiceStateLabel {
    font-size: 16px;
  }
  .pt-buddy-dock-primary .pt-buddy-dock-subcopy {
    display: block;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pt-buddy-dock-primary .pb-face-ring::before,
  .pt-buddy-dock-primary .pb-face-img,
  .pt-buddy-dock-primary .pb-voice-bars span {
    animation: none;
  }
  .pt-buddy-dock-primary .pb-face-ring::before {
    box-shadow: 0 0 0 3px rgba(240, 171, 78, 0.2);
  }
  .pt-buddy-dock-primary .pb-voice-bars span {
    height: 6px;
  }
}
/* Compact one-line mode/field summary - "always display", never more
   than a single line, hidden entirely until there's anything real to
   summarize (no mode chosen yet AND both fields empty). */
.pt-buddy-dock-summary {
  margin: 6px 0 0;
  font-size: 11px;
  color: var(--pt-ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pt-buddy-companion-face {
  display: flex; align-items: center; gap: 8px; justify-content: center;
  width: 100%; background: none; border: none; padding: 0; margin: 0;
  cursor: pointer; font: inherit; color: inherit;
}
/* PT-LAB-20260903-008: SPEAKING INDICATOR. --pb-ring-color/--pb-ring-glow
   are the only things each .pb-state-* rule below sets - the ring's own
   box model, position, and the img inside it never change, so switching
   states can never shift layout or obstruct the label/buttons beside it
   (a fixed 32x32 slot regardless of state). Ring color reuses the
   existing warm --pt-accent token (already amber, #f0ab4e) rather than
   introducing a new one. */
.pb-face-ring {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  flex: none;
}
.pb-face-ring::before {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  border: 2px solid transparent;
  box-shadow: 0 0 0 0 transparent;
  transition: border-color 200ms ease;
}
.pb-face-img {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  position: relative;
  z-index: 1;
}
/* PT-LAB-20260904-014: "one primary microphone control" - a small mic
   glyph badge anchored to the ring's own corner, marking the rail's one
   real affordance without adding a second competing button. Decorative
   only (aria-hidden in the markup); the face button's own aria-label
   already carries the meaning for assistive tech. */
.pb-mic-icon {
  position: absolute;
  z-index: 2;
  right: -2px;
  bottom: -2px;
  background: var(--pt-surface);
  border-radius: 50%;
  padding: 2px;
  color: var(--pt-accent);
}
/* Listening: calm "breathing" - slow (4s), low-amplitude, never a hard
   on/off flash. */
.pb-state-listening .pb-face-ring::before {
  border-color: rgba(240, 171, 78, 0.32);
  animation: pb-breathe 4s ease-in-out infinite;
}
/* Speaking: the warm amber pulsing ring/glow the design asks for -
   brisker than listening's breathing so the two states read as
   distinct, but still a smooth ease (well under 1 cycle/sec - nowhere
   near a strobe/flash rate) rather than a blink. */
.pb-state-speaking .pb-face-ring::before {
  border-color: var(--pt-accent);
  animation: pb-pulse 1.8s ease-in-out infinite;
}
@keyframes pb-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240, 171, 78, 0.16); }
  50% { box-shadow: 0 0 0 4px rgba(240, 171, 78, 0.05); }
}
@keyframes pb-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240, 171, 78, 0.5); }
  50% { box-shadow: 0 0 0 6px rgba(240, 171, 78, 0.14); }
}
/* Voice-level bars: zero width/hidden except while actually speaking,
   so they never obstruct the label text in any other state. Heights are
   set inline by the real analyser-driven meter loop (porch-buddy-
   voice.js); this only defines their box model and color. */
.pb-voice-bars {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 14px;
  width: 0;
  overflow: hidden;
  flex: none;
  transition: width 160ms ease;
}
.pb-state-speaking .pb-voice-bars {
  width: 20px;
}
.pb-voice-bars span {
  width: 3px;
  min-height: 3px;
  height: 3px;
  border-radius: 2px;
  background: var(--pt-accent);
  transition: height 90ms ease;
}
/* Reduced motion: every state stays visually distinguishable (a
   brighter/heavier ring for Speaking than Listening) with zero ongoing
   animation - a fixed ring and fixed-height bars, never a moving one. */
@media (prefers-reduced-motion: reduce) {
  .pb-face-ring::before { transition: none; }
  .pb-state-listening .pb-face-ring::before {
    animation: none;
    border-color: rgba(240, 171, 78, 0.3);
  }
  .pb-state-speaking .pb-face-ring::before {
    animation: none;
    border-color: var(--pt-accent);
    box-shadow: 0 0 0 3px rgba(240, 171, 78, 0.22);
  }
  .pb-voice-bars { transition: none; }
  .pb-voice-bars span { transition: none; height: 8px !important; }
}
/* MOBILE DOCK compactness (PT-LAB-20260903-002): Resume/Try Again/
   Continue read as heavier than the rest of this compact status bar at
   the default button padding - trimmed here, still >=44px tall (the
   global `button` rule's own min-height, untouched) so the tap target
   stays accessible; only the padding/type shrink. */
.pt-buddy-dock-action {
  padding: 8px 14px;
  font-size: 13px;
}
@media (min-width: 1100px) {
  /* Compact two-column lockup: emblem left, headline/sub-copy right,
     vertically centered against the emblem's own box (align-items:
     center on the row does this without a manual offset). Text
     switches to left-aligned - a left-flush reading column beside the
     emblem, not centered under it like the mobile/tablet stack. */
  .pt-arrival-lockup {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 24px;
    text-align: left;
  }
  .pt-arrival-emblem { width: clamp(180px, 14vw, 210px); max-width: 210px; }
}

/* --- the single dominant idea input --- */
.pt-prompt { display: flex; flex-direction: column; gap: 8px; }
.pt-prompt .textarea-with-mic {
  border: 1.5px solid var(--pt-line);
  background: var(--pt-surface);
  border-radius: var(--pt-radius);
  padding: 18px 18px 14px;
  box-shadow: var(--pt-shadow-soft);
}
.pt-prompt textarea {
  width: 100%;
  min-height: 96px;
  border: none;
  background: transparent;
  resize: vertical;
  color: var(--pt-ink);
  font: 420 20px/1.45 var(--pt-serif);
  font-optical-sizing: auto;
}
.pt-prompt textarea:focus { outline: none; }
.pt-prompt textarea::placeholder { color: var(--pt-placeholder); }
.pt-prompt .textarea-with-mic:focus-within {
  border-color: var(--pt-accent);
  box-shadow: 0 0 0 3px rgba(240, 171, 78, 0.22);
}
.pt-prompt .char-count { color: var(--pt-ink-faint); font: 500 12.5px var(--pt-sans); }
.pt-prompt .prompt-only-notice {
  border: 1px solid rgba(240, 171, 78, 0.34);
  background: rgba(240, 171, 78, 0.07);
  border-radius: var(--pt-radius-sm);
  padding: 12px 14px;
  color: var(--pt-ink-soft);
  font: 400 13.5px/1.55 var(--pt-sans);
}
.pt-prompt .prompt-only-notice strong { color: var(--pt-accent); }

/* --- example ideas: wrap, never scroll sideways --- */
.pt-chip-rail { display: flex; flex-wrap: wrap; gap: 8px; }
.pt-chip {
  display: inline-flex;
  align-items: center;
  min-height: var(--pt-tap);
  padding: 12px 16px;
  border-radius: 100px;
  border: 1px solid var(--pt-line);
  background: var(--pt-surface);
  color: var(--pt-ink-soft);
  font: 500 13.5px var(--pt-sans);
  cursor: pointer;
  max-width: 100%;
}
.pt-chip:hover { border-color: var(--pt-accent); color: var(--pt-ink); }

/* --- commitment --- */
.pt-commit { display: flex; flex-direction: column; align-items: stretch; gap: 10px; }
.pt-commit .pt-cta { width: 100%; }
.pt-commit .pill {
  align-self: center;
  background: none;
  border: none;
  padding: 0;
  color: var(--pt-ink-faint);
  font: 500 13px var(--pt-sans);
}
.pt-commit .pill b { color: var(--pt-ink); font-variant-numeric: tabular-nums; }

/* PT-LAB-20260830-008: hidden (display: none below) - real owner
   report, "expands controls that aren't useful/active." Confirmed:
   every control inside #ptCustomize is genuinely inert (screens.js's
   disableCustomize() disables every button/input/select/textarea in
   that panel - Customize is uncertified for every model, not a
   per-provider gap). Hiding the entry point rather than deleting it or
   the panel underneath - re-enable by removing the display:none line
   once controls are certified per model, no other change needed. */
/* PT-LAB-20260903-001, owner decision: understated by design - Porch
   Buddy (the dock's own Inactive-state "Create with Porch Buddy"
   button, .pt-buddy-dock's own styling above) is the primary entry;
   this is the secondary fallback for microphone denial, accessibility,
   unsupported devices, typed use, or preference. Same visual language
   as .pt-customize-toggle just below (underlined text-link, not a
   filled button) but visible by default (that one is display:none,
   feature-gated elsewhere) - always reachable, never the loud option. */
.pt-create-manually-toggle {
  display: flex;
  align-self: center;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--pt-tap);
  min-width: var(--pt-tap);
  margin: 4px auto 0;
  padding: 10px 14px;
  background: none;
  border: none;
  color: var(--pt-ink-soft);
  font: 500 13px var(--pt-sans);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(255, 255, 255, 0.25);
  cursor: pointer;
}
.pt-customize-toggle {
  display: none;
  align-self: center;
  align-items: center;
  gap: 8px;
  min-height: var(--pt-tap);
  padding: 10px 14px;
  background: none;
  border: none;
  color: var(--pt-accent);
  font: 600 14px var(--pt-sans);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(240, 171, 78, 0.45);
  cursor: pointer;
}

/* --- progressive disclosure groups --- */
.pt-customize {
  max-width: 720px;
  margin: 0 auto var(--pt-gap-xl);
  /* PT-LAB-20260907-046: was `0 max(22px, --pt-safe-l) 0 max(22px, --pt-safe-r)`
     - the padding shorthand's right/left slots had the safe-area variables
     swapped (right padding read --pt-safe-l, left padding read --pt-safe-r),
     backwards from .pt-arrival's own correct pattern just above. Invisible
     on a normal portrait test (both env() values are 0px there, so
     max(22px, 0) is 22px either way), but a real asymmetric-inset bug on a
     notched device in landscape, where left/right safe-area insets genuinely
     differ. */
  padding: 0 max(22px, var(--pt-safe-r)) 0 max(22px, var(--pt-safe-l));
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}
.pt-customize[hidden] { display: none; }
.pt-group {
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
  background: var(--pt-surface);
  overflow: hidden;
}
.pt-group > summary {
  list-style: none;
  cursor: pointer;
  min-height: var(--pt-tap);
  display: flex;
  align-items: center;
  padding: 12px 18px;
  color: var(--pt-ink);
  font: 600 15px var(--pt-sans);
}
.pt-group > summary::-webkit-details-marker { display: none; }
.pt-group > summary::after {
  content: "+";
  margin-left: auto;
  color: var(--pt-accent);
  font-size: 18px;
  line-height: 1;
}
.pt-group[open] > summary::after { content: "\2013"; }
/* PT-LAB-20260907-046: the 14px left/right padding here was an extra
   inset on top of .pt-customize's own 22px edge padding, absent from
   .pt-prompt (which has none of its own) - Genre/Mood/Instruments/
   Vocals/Porch Mixer cards rendered ~30px narrower and shifted ~15px
   right of Description/Lyrics/Quality/Make My Song directly above them,
   instead of sharing the same left/right edges. Left/right zeroed so
   these cards align with the rest of the creation page; the group's own
   visible 1px border (.pt-group, unchanged) still frames them exactly
   as before. Bottom padding (vertical spacing before the collapsible's
   own edge) is untouched. */
.pt-group-body { padding: 0 0 14px; display: flex; flex-direction: column; gap: 12px; }
.pt-group-body:empty { display: none; }
.pt-group-sub { margin-left: 8px; font: 400 12px var(--pt-sans); color: var(--text-muted); }

/* --- warm coaching, shown beside the control it concerns --- */
.pt-coach {
  border-left: 3px solid var(--pt-accent);
  background: rgba(240, 171, 78, 0.07);
  border-radius: 0 var(--pt-radius-sm) var(--pt-radius-sm) 0;
  padding: 11px 14px;
  color: var(--pt-ink-soft) !important;
  font: 400 13.5px/1.55 var(--pt-sans);
}

/* The retired mascot stage is hidden, not deleted - its markup, videos and
   beat-sync code all remain in place and functional. */
.hero-video-frame { display: none; }

@media (min-width: 700px) {
  .pt-arrival { gap: var(--pt-gap-xl); padding-top: var(--pt-gap-xl); }
  /* .pt-headline's size is no longer set here - PT-LAB-20260830-024's
     single clamp(34px, 5vw, 56px) on the base rule already scales
     smoothly through this breakpoint (a fixed override here would just
     fight the clamp for no benefit). */
  .pt-prompt textarea { font-size: 21px; min-height: 110px; }
}
@media (min-width: 1100px) {
  .pt-arrival { max-width: 780px; }
  .pt-prompt textarea { min-height: 124px; }
  .pt-commit { flex-direction: row; align-items: center; gap: 18px; }
  .pt-commit .pt-cta { width: auto; min-width: 250px; }
  .pt-commit .pill { align-self: auto; }
  .pt-customize { max-width: 780px; }
}

/* --- slate foundation applied to the existing shell -----------------------
   The old blurred photo backdrop is retired in favour of the dark slate
   system. Its markup and rules are left intact above; only its visibility
   changes here. */
.bg-photo, .bg-tint, .bg-vignette { display: none; }
body { background: var(--pt-bg); color: var(--pt-ink); }
/* Full-bleed fix: a pre-Phase4 desktop rule (the old left-rail layout,
   ~line 522) still set .page's left padding to 96px with no matching
   right padding - that layout's own content has long since moved inside
   .title-block via screens.js's move() calls, so nothing actually fills
   that old rail anymore, but the lopsided 96px/16px gutter remained,
   reading as a boxed-in/offset outer frame around the whole hero.
   Zeroing side padding here (later in the cascade, same specificity,
   so it wins) lets .pt-hero/.pt-shell run edge-to-edge as intended -
   vertical padding (top clearance, bottom clearance for the tab bar) is
   untouched, and every creation card keeps its own comfortable margin
   from .pt-arrival's own max-width/padding regardless of this. */
.page { background: var(--pt-bg); padding-left: 0; padding-right: 0; }
@media (min-width: 900px) {
  .page { padding-left: 0; }
}
/* Explicit outer-shell reset: static inspection of every rule touching
   html/body/.page/.panel/.pt-shell found no border/radius/shadow/outline
   set on any of them (the "giant rounded card" look traced instead to
   .page's asymmetric padding above), but this is a zero-risk defensive
   reset on exactly those five containers in case something inherited or
   browser-default was still producing a visible perimeter - a no-op
   wherever nothing was actually set, and does not touch any internal
   card's own border/radius (.glass, .pt-video-cta, modals, chips, etc.
   are untouched). */
html, body, .page, .panel, .pt-shell {
  border: 0; border-radius: 0; box-shadow: none; outline: none;
}
.panel { max-width: 100%; gap: 0; }
.tagline { color: var(--pt-ink-soft); }

/* button.primary is (0,1,1); match it so the approved gradient wins without
   !important and without touching the original rule. */
button.pt-cta {
  background: var(--pt-cta-gradient);
  color: var(--pt-cta-ink);
  font: 700 16px/1 var(--pt-sans);
  box-shadow: 0 10px 24px rgba(120, 80, 40, 0.38);
  min-height: var(--pt-tap);
  padding: 15px 26px;
  border-radius: 100px;
  border: 0;
}
button.pt-cta:disabled {
  background: var(--pt-surface-2);
  color: var(--pt-ink-faint);
  box-shadow: none;
  cursor: not-allowed;
}

/* The re-homed cards keep their own ids and rules; only their frame is
   restated so they sit correctly inside the Customize groups. */
.pt-group-body .glass,
.pt-group-body .card-frame {
  background: var(--pt-surface-2);
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius-sm);
  backdrop-filter: none;
}

/* ---------------------------------------------------------------------------
   POST-SUBMIT SCREENS (Batch 4)
   Screen visibility is driven by <body data-screen>. Every control inside is
   a re-homed existing element.
   ------------------------------------------------------------------------ */

.pt-screen { display: none; }
body[data-screen="song-generating"] .pt-screen[data-screen-only="song-generating"],
body[data-screen="video-generating"] .pt-screen[data-screen-only="video-generating"],
body[data-screen="song-result"]     .pt-screen[data-screen-only="song-result"],
body[data-screen="video-result"]    .pt-screen[data-screen-only="video-result"] { display: block; }

/* Arrival + Customize belong to the arrival screen only. */
/* social-row/pt-store-row/pt-legal-row used to be hidden here too, back
   when they sat directly in the arrival page flow - they now live inside
   #ptMenuSheet (screens.js), reachable from every screen via the
   persistent top-bar hamburger, so they must stay unaffected by which
   screen is active. */
body[data-screen]:not([data-screen="arrival"]) .pt-arrival,
body[data-screen]:not([data-screen="arrival"]) .pt-customize,
body[data-screen]:not([data-screen="arrival"]) .tagline { display: none; }
/* PT-LAB-20260830-024: the inverse of the rule above - .pt-logo-hero
   (the persistent, non-arrival brand mark near the header) hides only
   on the arrival screen, which shows its own distinct .pt-arrival-emblem
   inside the lockup instead. Together with .pt-arrival-emblem being
   scoped inside .pt-arrival (hidden everywhere else by the rule above),
   this guarantees exactly one emblem is visible on any given screen. */
body[data-screen="arrival"] .pt-logo-hero { display: none; }

.pt-screen:focus { outline: none; }
[data-screen-head]:focus { outline: none; }

.pt-screen {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--pt-gap-lg) max(22px, var(--pt-safe-l))
           calc(var(--pt-gap-xl) + var(--pt-safe-b)) max(22px, var(--pt-safe-r));
  width: 100%;
}

/* ---------------- Porch Buddy (PT-LAB-20260830-015/022) --------------
   Official, owner-approved artwork (web/assets/porch-buddy/) - a real
   transparent-PNG character, not the abstract Sound Bloom/programmatic
   face it replaces on the generating screen (now fully removed - see
   PT-LAB-20260830-021), and a genuinely new presence on arrival/result.
   Sized up from the first pass (real owner report: arrival and result
   were both "too small") while still staying a companion to the actual
   content (headline, player, actions), not competing hero art - sizes
   below are tuned so it can never plausibly overlap the logo, headline,
   menu, or result content in normal document flow (each instance is a
   block-level image, never absolutely positioned). object-fit: contain
   + explicit width/height attributes on the <img> tags themselves
   reserve the right aspect ratio before the image loads, so there's no
   layout shift either. */
.pt-porch-buddy { display: block; height: auto; object-fit: contain; }
.pt-porch-buddy-arrival { width: 140px; margin: 0 0 2px; }
.pt-porch-buddy-generating { width: 150px; }
.pt-porch-buddy-result { width: 170px; margin-bottom: 8px; }
@media (min-width: 700px) {
  .pt-porch-buddy-arrival { width: 168px; }
  .pt-porch-buddy-generating { width: 178px; }
  .pt-porch-buddy-result { width: 200px; }
}
@media (min-width: 1100px) {
  .pt-porch-buddy-arrival { width: 190px; }
  .pt-porch-buddy-generating { width: 210px; }
  .pt-porch-buddy-result { width: 220px; }
}

/* PT-LAB-20260830-022: restrained, zero-cost motion (pure CSS transform
   animations against the existing static PNGs - no sprite sheets, no
   video, no new dependency). transform-only so nothing here ever
   triggers layout/reflow. Each screen gets its own distinct, gentle
   character:
   - idle (arrival): a slow breathing/floating loop - present, alive,
     never distracting from the headline/CTA above the fold.
   - listening (generation): a slightly quicker, restrained attentive
     sway - reads as "paying attention," calm, never frantic.
   - complete (result): ONE bounce-in entrance (not infinite) - a brief
     celebratory arrival, then it settles and stays still, matching "the
     job's done" rather than perpetual motion competing with the player/
     actions below it. */
@keyframes pbFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}
@keyframes pbAttentiveSway {
  0%, 100% { transform: rotate(0deg) translateY(0); }
  50%      { transform: rotate(-1.5deg) translateY(-3px); }
}
@keyframes pbBounceIn {
  0%   { transform: scale(0.7) translateY(14px); opacity: 0; }
  55%  { transform: scale(1.06) translateY(-5px); opacity: 1; }
  75%  { transform: scale(0.97) translateY(1px); }
  100% { transform: scale(1) translateY(0); }
}
.pt-porch-buddy-arrival { animation: pbFloat 3.6s ease-in-out infinite; }
.pt-porch-buddy-generating { animation: pbAttentiveSway 2.8s ease-in-out infinite; }
.pt-porch-buddy-result { animation: pbBounceIn 0.7s ease-out 1; }
@media (prefers-reduced-motion: reduce) {
  .pt-porch-buddy-arrival, .pt-porch-buddy-generating, .pt-porch-buddy-result {
    animation: none;
  }
}

/* Deterministic Porch Buddy speech bubbles (PT-LAB-20260830-022) - zero-
   LLM-cost, tied only to real state (see app.js's use of porch-buddy.js's
   PORCH_BUDDY_PHRASES). Non-blocking: a plain inline bubble in normal
   document flow under the character, never covering any control - no
   position:absolute/fixed overlay anywhere in this rule. */
.pt-porch-buddy-bubble {
  display: block;
  max-width: 260px;
  margin: 6px 0 10px;
  padding: 8px 14px;
  border-radius: var(--pt-radius-sm);
  background: var(--pt-surface);
  border: 1px solid var(--pt-line);
  color: var(--pt-ink);
  font: 400 13.5px/1.45 var(--pt-sans);
}
.pt-porch-buddy-bubble:empty { display: none; }

/* ---------------- generating (song + video share the treatment) ---------- */
.pt-gen-stage { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; }
/* PT-LAB-20260907-045: .pt-gen-stage's own align-items: flex-start left-
   aligns every child, including song-generating's Porch Buddy image -
   the video-generating screen (sharing this same class, no Buddy image)
   is untouched by scoping the override to the image's own class alone,
   never .pt-gen-stage itself. Title/status/note text keeps its existing
   left alignment - only the character is centered. */
.pt-porch-buddy-generating { align-self: center; }
.pt-gen-title {
  font: 600 27px/1.22 var(--pt-serif);
  margin: 4px 0 0;
  color: var(--pt-ink);
}
.pt-gen-expect { margin: 0; color: var(--pt-ink-soft); font: 400 15px/1.55 var(--pt-sans); }
/* app.js's own #status line - the only progress copy shown. No percentage,
   no fabricated stage list, no timer this backend does not expose. */
.pt-gen-status { color: var(--pt-ink-soft); font: 500 14.5px/1.6 var(--pt-sans); min-height: 20px; }
.pt-gen-status .progress-track { margin-top: 10px; }
.pt-gen-note { margin: 0; color: var(--pt-ink-faint); font: 400 13.5px var(--pt-sans); }
.pt-gen-aside { display: none; }
.pt-gen-aside-label {
  margin: 0 0 10px; color: var(--pt-ink-faint);
  font: 700 11px var(--pt-sans); letter-spacing: .14em; text-transform: uppercase;
}
.pt-gen-recap {
  border: 1px solid var(--pt-line); background: var(--pt-surface);
  border-radius: var(--pt-radius); padding: 18px 20px;
  color: var(--pt-ink-soft); font: 400 14px/1.6 var(--pt-sans);
}

/* ---------------- result ---------------- */
.pt-eyebrow {
  margin: 0 0 6px; color: var(--pt-ink-faint);
  font: 700 11px var(--pt-sans); letter-spacing: .14em; text-transform: uppercase;
}
.pt-result-title { font: 600 27px/1.2 var(--pt-serif); margin: 0 0 12px; color: var(--pt-ink); }
.pt-result-meta { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: var(--pt-gap); }
.pt-result-meta:empty { display: none; }
.pt-player { margin-bottom: var(--pt-gap); }
.pt-player audio { width: 100%; display: block; }
.pt-song-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: var(--pt-gap-lg); }
.pt-song-actions button {
  flex: 1 1 auto; min-width: 120px; min-height: var(--pt-tap);
  border-radius: 100px; border: 1px solid var(--pt-line);
  background: var(--pt-surface); color: var(--pt-ink);
  font: 600 14px var(--pt-sans); cursor: pointer; padding: 12px 16px;
}
.pt-song-actions button:disabled { color: var(--pt-ink-faint); cursor: not-allowed; }

/* Start a New Song / Tweak This Song - the two obvious next steps once
   there's something to listen to. Previously two `.pt-quiet` (underlined
   text-link style, indistinguishable from a footnote) buttons stacked at
   the very BOTTOM of the screen, below the entire Music Video upsell
   card - easy to miss entirely on a screen whose whole point is "you
   just finished listening, what now." Moved directly under the
   player/download row and given real button weight instead. Tweak is
   the primary path (same song, keep refining) so it gets the filled
   treatment; Start a new song stays visually quieter since it's the
   destructive one - screens.js's existing loss-confirmation modal on
   #ptNewSong is untouched, this only changes layout/styling. */
.pt-result-next { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: var(--pt-gap-lg); }
.pt-result-next-btn {
  flex: 1 1 200px; min-height: var(--pt-tap); border-radius: 100px;
  font: 600 14px var(--pt-sans); cursor: pointer; padding: 13px 20px;
}
.pt-result-next-primary {
  border: 0; background: var(--pt-cta-gradient); color: var(--pt-cta-ink); font-weight: 700;
}
.pt-result-next-quiet {
  border: 1px solid var(--pt-line); background: transparent; color: var(--pt-ink-soft);
}

/* ---------------- Music Video: the second act ---------------- */
.pt-video-cta {
  position: relative;
  border-radius: var(--pt-radius-lg);
  border: 1px solid rgba(255,255,255,.12);
  background: linear-gradient(158deg, #14293a 0%, #0c1620 100%);
  box-shadow: 0 18px 40px rgba(8,18,24,.38);
  padding: 20px;
  margin-bottom: var(--pt-gap-lg);
}
.pt-video-preview {
  border-radius: 14px; aspect-ratio: 16/9; position: relative; overflow: hidden;
  margin-bottom: 18px;
  background-image: var(--pt-hero); background-size: cover; background-position: center 60%;
}
.pt-video-preview::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(8,18,24,.10) 0%, rgba(8,18,24,.58) 100%),
    radial-gradient(70% 60% at 50% 92%, rgba(232,176,106,.34) 0%, rgba(232,176,106,0) 70%);
}
.pt-video-play {
  position: absolute; z-index: 2; top: 50%; left: 50%; transform: translate(-50%,-50%);
  width: 52px; height: 52px; border-radius: 50%; background: rgba(255,255,255,.94);
  box-shadow: 0 8px 20px rgba(20,20,24,.3);
}
.pt-video-play::before {
  content: ""; position: absolute; top: 50%; left: 54%; transform: translate(-50%,-50%);
  border-left: 15px solid #0e2129; border-top: 9px solid transparent; border-bottom: 9px solid transparent;
}
.pt-video-cta h3 { color: #fff; font: 600 21px/1.25 var(--pt-serif); margin: 0 0 8px; }
.pt-video-cta p { color: rgba(238,244,246,.86); font: 400 14px/1.55 var(--pt-sans); margin: 0 0 18px; }
.pt-video-meta { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 18px; }
.pt-video-meta span { display: block; font: 400 12px var(--pt-sans); color: rgba(238,244,246,.7); }
.pt-video-meta b { display: block; font: 700 16px var(--pt-sans); color: #fff; margin-top: 3px; }
/* Retained local video download styling. */
.pt-video-cta-host button {
  width: 100%; min-height: var(--pt-tap); border: 0; border-radius: 100px;
  background: var(--pt-cta-gradient); color: var(--pt-cta-ink);
  font: 700 15px var(--pt-sans); padding: 15px 26px; cursor: pointer;
}
.pt-video-cta-host button:disabled {
  background: rgba(255,255,255,.10); color: rgba(238,244,246,.45); cursor: not-allowed;
}
.pt-video-cta-host button span { opacity: .8; font-weight: 600; }

.pt-quiet {
  display: block; width: 100%; min-height: var(--pt-tap);
  background: none; border: none; color: var(--pt-accent);
  font: 600 14px var(--pt-sans); cursor: pointer; padding: 12px;
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: rgba(240,171,78,.45);
}

.pt-video-result video { width: 100%; border-radius: var(--pt-radius); display: block; }
.pt-video-result button {
  width: 100%; min-height: var(--pt-tap); margin-top: 12px;
  border-radius: 100px; border: 1px solid var(--pt-line);
  background: var(--pt-surface); color: var(--pt-ink);
  font: 600 14px var(--pt-sans); cursor: pointer; padding: 12px 16px;
}

/* ---------------- Lab drawer ---------------- */
.pt-lab {
  max-width: 720px; margin: 0 auto var(--pt-gap-xl);
  padding: 0 max(22px, var(--pt-safe-l));
  border-top: 1px dashed rgba(255,255,255,.18);
}
.pt-lab[hidden] { display: none; }
.pt-lab > summary {
  cursor: pointer; list-style: none; min-height: var(--pt-tap);
  display: flex; align-items: center;
  color: var(--pt-ink-faint); font: 600 12px var(--pt-sans);
  letter-spacing: .1em; text-transform: uppercase;
}
.pt-lab > summary::-webkit-details-marker { display: none; }
.pt-lab-body { padding-bottom: var(--pt-gap-lg); }

/* ---------------- responsive ---------------- */
@media (min-width: 700px) {
  .pt-gen-title { font-size: 32px; }
  .pt-result-title { font-size: 33px; }
}
@media (min-width: 1100px) {
  /* Desktop generating uses the canvas: stage left, recap right - not an
     enlarged mobile screen ringed by dead space. */
  .pt-screen { max-width: 1080px; }
  /* Scoped to the ACTIVE screen. An unscoped `.pt-gen{display:grid}` here
     would out-order the `.pt-screen{display:none}` base rule and stack every
     screen at once - caught in desktop verification. */
  body[data-screen="song-generating"]  .pt-gen[data-screen-only="song-generating"],
  body[data-screen="video-generating"] .pt-gen[data-screen-only="video-generating"] {
    display: grid; grid-template-columns: 1fr 340px; gap: 56px; align-items: start;
  }
  /* PT-LAB-20260830-010: song-generating goes single-column - its own
     "Your song so far" aside (#ptGenRecap) was removed from the markup
     entirely (real owner report, "does nothing and adds no value" -
     confirmed: nothing in app.js or screens.js ever wrote to it, a
     permanently-empty panel). video-generating keeps its own real aside
     (#ptVideoGenRecap) and the shared 1fr 340px split above - this
     override is scoped to song-generating only so that stays untouched. */
  body[data-screen="song-generating"] .pt-gen[data-screen-only="song-generating"] {
    grid-template-columns: 1fr;
  }
  .pt-gen-aside { display: block; }
  .pt-gen-title { font-size: 38px; }
  /* No more decorative left column (the old .pt-result-art gradient
     block never held real content) - the result screen is a single
     focused reading column, capped narrower than the full 1080px canvas
     so a finished song doesn't read as a sparse two-column layout with
     one column empty. */
  body[data-screen="song-result"] .pt-result[data-screen-only="song-result"],
  body[data-screen="video-result"] .pt-result[data-screen-only="video-result"] {
    max-width: 640px; margin: 0 auto;
  }
  .pt-lab { max-width: 1080px; }
}

/* Screen-1 rebuild: lyrics mode chips + the disabled Customize panel.
   Customize's controls are genuinely disabled (see screens.js compose())
   - this is just making that visually honest, not decorative. */
.pt-lyrics-modes { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.pt-mode-chip { font-size: 13px; font-weight: 600; padding: 8px 16px; }
.pt-lyrics-modes .chip:disabled { opacity: .45; cursor: not-allowed; }
.pt-soon {
  display: inline-block; margin-left: 6px; font-size: 10px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; opacity: .75;
}
.pt-chip-sub { display: block; margin-top: 2px; font-size: 11px; font-weight: 500; opacity: .8; }
.pt-sample-prompts { margin-top: 10px; }
.pt-sample-prompts-label { font-size: 12px; color: var(--text-muted); margin: 0 0 8px; }
.pt-preset-chip {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; padding: 10px 14px;
}
.pt-preset-name { font-weight: 700; font-size: 13px; }
.pt-preset-tags { font-size: 11px; color: var(--text-muted); }
.pt-preset-chip:hover .pt-preset-tags { color: var(--pt-ink); }

/* Starting point / Quality: same polished pill treatment as lyrics
   modes - one shared visual language for every "pick one of these"
   control on the creation screen. */
.pt-starting-point, .pt-quality { margin: 4px 0 10px; }
.pt-quality-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-muted); margin: 0 0 8px;
}
/* PT-LAB-20260830-009: split from .pt-quality-label above - real owner
   report ("Starting Point currently visually disappears/blends into
   the page"). This is the user's first real decision (idea / my lyrics
   / instrumental), not a secondary label like Quality below it, so it
   gets real weight and ink-level contrast instead of the same small
   muted-gray treatment - modest (still a plain text label, same
   uppercase/letter-spacing language, no icon/background/border added). */
.pt-starting-point-label {
  font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--pt-ink); margin: 0 0 8px;
}
#detailsCard.pt-secondary { padding: 14px 22px; }
#detailsCard.pt-secondary #detailsHeading { font-size: 15px; opacity: .85; }
/* Stronger separation for the one card whose content is literally what
   gets sung - a warm accent edge, not a redesign of the shared .glass
   card language every other card still uses. */
#lyricsCard { border-left: 3px solid var(--copper); }
#lyricsLabel { font-weight: 600; color: #fff; margin-bottom: 4px; display: block; }
/* PT-LAB-20260830-019: real, screenshot-confirmed bug (owner report -
   "helper/instruction text overlaps the lyrics input... first lines of
   entered lyrics are obscured... input boundary and actual typing area
   are unclear"). Root cause: #lyrics is a bare <textarea>, not wrapped
   in .textarea-with-mic the way the Song Description field is - but
   .pt-prompt textarea (styles.css, the shared reset for every textarea
   inside .pt-prompt) still applies to it, setting background:transparent
   and border:none. Those resets are only safe on a textarea that HAS a
   .textarea-with-mic wrapper supplying the actual visible box/padding -
   #lyrics has no such wrapper, so it rendered with no border, no
   background of its own, and no padding, directly on top of whatever
   #lyricsCard's own surface was - exactly the "unclear boundary,
   overlapping content" symptom described. Giving #lyrics its own
   explicit box here (an ID selector, so it correctly wins over the
   lower-specificity .pt-prompt textarea reset with no !important
   needed) instead of adding a wrapper element, to avoid touching
   index.html structure or JS that already references #lyrics by id. */
#lyrics {
  display: block;
  width: 100%;
  min-height: 220px;
  margin-top: 6px;
  padding: 16px 18px;
  border: 1.5px solid var(--pt-line);
  border-radius: var(--pt-radius-sm);
  background: var(--pt-surface);
  color: var(--pt-ink);
  font: 400 16px/1.6 var(--pt-sans);
  resize: vertical;
  box-sizing: border-box;
}
#lyrics::placeholder { color: var(--pt-placeholder); }
#lyrics:focus {
  outline: none;
  border-color: var(--pt-accent);
  box-shadow: 0 0 0 3px rgba(240, 171, 78, 0.22);
}
@media (min-width: 700px) {
  #lyrics { min-height: 260px; }
}

/* PT-LAB-20260830-020: "Get the sung lyrics as text" - real bug, still
   visible after Tweak (and, in fact, for the entire normal Standard/
   Premium creation flow, not just after Tweak - see updateTierUI() in
   app.js for the full trace). The `hidden` HTML attribute on
   #transcribeLyricsLabel was never actually enough on its own: that
   element also carries class="switch-row", and .switch-row's own
   `display: flex` (an AUTHOR stylesheet rule) beats the browser's
   default `[hidden] { display: none }` UA-stylesheet rule regardless of
   selector specificity - author CSS always wins over UA CSS at the
   cascade-origin level, full stop. An ID selector here is what actually
   fixes it: #transcribeLyricsLabel's specificity (1,0,0) beats
   .switch-row's (0,1,0) within the SAME (author) origin, so this
   genuinely wins the cascade rather than just happening to not conflict
   - no !important needed, and it can never be silently re-shown by a
   later style.display="" the way the old JS-only approach was. */
#transcribeLyricsLabel { display: none; }

.pt-customize.pt-disabled { opacity: .55; }
.pt-customize.pt-disabled button:disabled,
.pt-customize.pt-disabled input:disabled,
.pt-customize.pt-disabled select:disabled { cursor: not-allowed; }

/* ---------- Consolidated creation-experience pass ---------- */

/* Top bar: hamburger (left) / brand (center, unchanged) / credits +
   account (right). Reuses the existing 3-column .pt-hero-bar grid -
   no new bar, no duplicate brand mark. */
.pt-menu-toggle, .pt-topbar-account {
  justify-self: start;
  width: 48px; height: 48px; min-width: 44px; min-height: 44px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: rgba(12,13,20,0.72); border: 1.5px solid rgba(255,255,255,0.4);
  color: #fff; flex: none;
  box-shadow: 0 2px 10px rgba(0,0,0,0.45);
}
/* Was too subtle against a busy photo background at 20px/40% border -
   larger icon and stronger border/shadow make it read as a real control
   at a glance, not decoration. PT-LAB-20260902-010: bumped again (44px
   minimum tap target, per WCAG 2.5.5, with real margin above it at
   48px) plus a stronger border/background for contrast on light photo
   backgrounds specifically, not just the average case. */
.pt-menu-toggle svg, .pt-topbar-account svg { width: 24px; height: 24px; stroke-width: 2.25; }
.pt-menu-toggle:hover, .pt-topbar-account:hover { background: rgba(12,13,20,0.8); border-color: var(--pt-accent); }
.pt-menu-sheet-head {
  display: flex; align-items: center; justify-content: space-between;
  font: 700 13px var(--pt-sans); color: var(--pt-ink); margin-bottom: 10px;
  padding-bottom: 10px; border-bottom: 1px solid var(--pt-line);
}
.pt-menu-close {
  width: 32px; height: 32px; border-radius: 50%; display: flex;
  align-items: center; justify-content: center; font-size: 20px; line-height: 1;
  background: rgba(255,255,255,0.06); border: 1px solid var(--pt-line); color: var(--pt-ink);
}
.pt-hero-slot-end { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.pt-hero-slot-end > .pt-topbar-route-bridge { display: none; }
.pt-topbar-account { justify-self: auto; }
#libraryOpenBtn { width: auto; padding: 0 15px; gap: 8px; border-radius: 999px; }
.pt-topbar-library-label { font: 700 12px var(--pt-sans); }
.pt-topbar-credits-host:empty { display: none; }
.pt-topbar-credits-host .pill {
  background: rgba(12,13,20,0.45); border: 1px solid rgba(255,255,255,0.16);
  color: #fff; font-size: 12px; padding: 8px 12px;
}
@media (max-width: 560px) {
  #libraryOpenBtn { width: 48px; padding: 0; border-radius: 50%; }
  .pt-topbar-library-label { display: none; }
}
/* position:fixed (not absolute) deliberately - .pt-hero (its would-be
   absolute-positioning ancestor) has overflow:hidden for its own
   sunset-photo crop, which would clip an absolutely-positioned dropdown
   taller than the hero itself. Fixed positioning is relative to the
   viewport instead, escaping that clipping regardless of DOM nesting or
   scroll position. */
.pt-menu-sheet {
  position: fixed; top: calc(var(--pt-safe-t) + 64px); left: var(--pt-safe-l);
  z-index: 50; max-width: min(320px, calc(100vw - 32px)); margin: 0 16px;
  background: var(--pt-surface); border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius); box-shadow: var(--pt-shadow);
  padding: 16px; max-height: calc(100vh - 100px); overflow-y: auto;
}
.pt-menu-sheet[hidden] { display: none; }
.pt-menu-sheet .social-row { margin-top: 0; justify-content: flex-start; }
.pt-menu-sheet .pt-store-row { justify-content: flex-start; }
.pt-menu-sheet .pt-legal-row { text-align: left; }

/* Arrival supporting copy, now living inside .pt-arrival-lockup-text
   directly under the headline (PT-LAB-20260830-024). clamp() matches
   the ticket's stated ranges: ~16-18px on mobile/tablet, ~18-20px at
   wide desktop. */
.pt-arrival-sub {
  margin: 6px 0 0; color: var(--pt-ink-soft);
  font: 400 clamp(16px, 1.6vw, 20px)/1.5 var(--pt-sans);
}

/* Starting-point cards: icon+title plus a short explanation line, still
   the same <button class="chip pt-mode-chip"> elements/listeners - only
   their internal markup and styling changed (added inner spans). */
.pt-mode-chip {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; border-radius: 14px; flex: 1 1 150px;
}
.pt-mode-chip-title { font-size: 13.5px; font-weight: 700; }
.pt-mode-chip-desc { font-size: 11px; font-weight: 500; opacity: .75; }

/* Voice Style / Duration: honest placeholders, not functional controls -
   aria-disabled and non-interactive by construction (a plain <div>, not
   a <select>), so there is no control here to accidentally wire up
   before it is certified. */
/* PT-LAB-20260830-008: hidden - real owner report ("Voice Style" and
   "Duration" currently do nothing). Confirmed: these were never real
   controls, just static "Auto (Recommended)" labels (aria-disabled,
   no input/select/button underneath) - nothing in onGenerate()'s
   request body reads a value from either. Hiding rather than deleting -
   the markup stays ready for whenever real per-model voice/duration
   selection is certified. */
.pt-voice-duration { display: none; gap: 10px; margin: 4px 0 10px; flex-wrap: wrap; }
.pt-vd-slot { flex: 1 1 160px; }
.pt-vd-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-muted); margin: 0 0 6px;
}
.pt-vd-optional { text-transform: none; letter-spacing: 0; opacity: .7; }
.pt-vd-value {
  border: 1px solid var(--pt-line); border-radius: 100px; padding: 10px 16px;
  font-size: 13px; font-weight: 600; color: var(--pt-ink-soft);
  background: rgba(255,255,255,0.03);
}
.pt-vd-rec { font-weight: 400; opacity: .7; }

.pt-quality-duration {
  margin: 8px 0 0;
  color: var(--pt-ink-soft);
  font: 600 12.5px var(--pt-sans);
}

.pt-lyrics-fit-summary {
  margin: 7px 2px 0;
  color: var(--text-muted);
  font-size: 12px;
  line-height: 1.4;
  text-align: right;
}
.pt-lyrics-fit-summary.warn { color: var(--copper); }
.pt-lyrics-fit-summary.over { color: #ff9d8a; font-weight: 700; }

/* CTA credit-cost note, fed by the existing cost-estimate fetch - no new
   calculation, just a second place the same number is shown. */
.pt-cta-cost {
  margin: 6px 0 0; text-align: center; font-size: 12.5px;
  color: var(--pt-ink-faint); min-height: 16px;
}

/* Song Description eyebrow label - sits above the existing, still
   mode-dependent heading text (set by app.js's setStartingMode). */
.pt-eyebrow-label {
  margin: 0 0 4px; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--pt-accent);
}

/* Porch Sauce: enhancement UX foundation only (see app.js) - deliberately
   styled as a secondary, non-alarming action, never the primary CTA. */
.pt-porch-sauce { margin-top: 10px; }
.pt-porch-sauce-btn {
  background: rgba(240,171,78,0.12); border: 1px solid rgba(240,171,78,0.35);
  color: var(--pt-accent-strong); font-weight: 700; font-size: 13px;
  padding: 9px 16px; border-radius: 100px; min-height: var(--pt-tap);
}
.pt-porch-sauce-note { margin: 6px 0 0; font-size: 12px; color: var(--pt-ink-faint); }
.pt-porch-sauce-status { margin: 4px 0 0; font-size: 12px; color: var(--pt-accent-strong); min-height: 14px; }
.pt-porch-sauce-undo-btn {
  margin-top: 4px; background: none; border: none; padding: 0;
  font-size: 12px; font-weight: 700; color: var(--pt-accent-strong);
  text-decoration: underline; min-height: var(--pt-tap);
}
/* Enhancement preview: shown only after a real backend result comes
   back, never applied to the Song Description field until the user
   explicitly clicks "Use this" - see app.js's click handler. */
.pt-porch-sauce-preview {
  margin-top: 8px; padding: 10px 12px; border-radius: 12px;
  background: rgba(240,171,78,0.08); border: 1px solid rgba(240,171,78,0.25);
}
.pt-porch-sauce-preview-label {
  margin: 0 0 4px; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--pt-ink-faint);
}
.pt-porch-sauce-preview-text { margin: 0 0 8px; font-size: 13px; line-height: 1.4; }
.pt-porch-sauce-preview-actions { display: flex; gap: 8px; }
.pt-porch-sauce-use-btn, .pt-porch-sauce-keep-btn {
  font-size: 12px; font-weight: 700; padding: 7px 14px; border-radius: 100px;
  min-height: var(--pt-tap);
}
.pt-porch-sauce-use-btn {
  background: var(--pt-accent-strong); border: 1px solid var(--pt-accent-strong); color: #1a1206;
}
.pt-porch-sauce-keep-btn {
  background: transparent; border: 1px solid rgba(240,171,78,0.35); color: var(--pt-accent-strong);
}

/* Porch Tune-Up (PT-LAB-20260906-041): a separate, non-voice Lyrics
   assistant beside the Lyrics editor - deliberately mirrors Porch
   Sauce's own classes/shape above exactly (same colors, same disabled/
   preview/undo pattern), never shares a class name with it so neither
   feature's own styling can ever drift by editing the other's rule. */
.pt-tune-up { margin-top: 10px; }
.pt-tune-up-btn {
  background: rgba(240,171,78,0.12); border: 1px solid rgba(240,171,78,0.35);
  color: var(--pt-accent-strong); font-weight: 700; font-size: 13px;
  padding: 9px 16px; border-radius: 100px; min-height: var(--pt-tap);
}
.pt-tune-up-btn:disabled { opacity: .45; cursor: not-allowed; }
.pt-tune-up-note { margin: 6px 0 0; font-size: 12px; color: var(--pt-ink-faint); }
.pt-tune-up-status { margin: 4px 0 0; font-size: 12px; color: var(--pt-accent-strong); min-height: 14px; }
.pt-tune-up-undo-btn {
  margin-top: 4px; background: none; border: none; padding: 0;
  font-size: 12px; font-weight: 700; color: var(--pt-accent-strong);
  text-decoration: underline; min-height: var(--pt-tap);
}
.pt-tune-up-panel {
  margin-top: 8px; padding: 10px 12px; border-radius: 12px;
  background: rgba(240,171,78,0.08); border: 1px solid rgba(240,171,78,0.25);
}
.pt-tune-up-panel-label {
  margin: 0 0 6px; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--pt-ink-faint);
}
.pt-tune-up-ops { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.pt-tune-up-op.active {
  background: var(--pt-accent-strong); border-color: var(--pt-accent-strong); color: #1a1206;
}
.pt-tune-up-instruction-label { display: block; font-size: 12px; color: var(--pt-ink-faint); margin-bottom: 4px; }
.pt-tune-up-instruction {
  width: 100%; box-sizing: border-box; padding: 8px 10px; border-radius: 8px;
  border: 1px solid var(--chip-border); background: rgba(255,255,255,0.05);
  color: inherit; font: inherit; font-size: 13px; min-height: var(--pt-tap);
}
.pt-tune-up-instruction-hint { margin: 4px 0 0; font-size: 11px; color: var(--pt-ink-faint); }
.pt-tune-up-panel-actions { display: flex; gap: 8px; margin-top: 8px; align-items: center; }
.pt-tune-up-apply-btn {
  background: var(--pt-accent-strong); border: 1px solid var(--pt-accent-strong); color: #1a1206;
  font-weight: 700; font-size: 13px; padding: 9px 18px; border-radius: 100px;
  min-height: var(--pt-tap);
}
.pt-tune-up-apply-btn:disabled { opacity: .45; cursor: not-allowed; }
/* Revision preview: shown only after a real backend result comes back,
   never applied to the Lyrics field until the user explicitly clicks
   "Use this" - see app.js's click handler. */
.pt-tune-up-preview {
  margin-top: 8px; padding: 10px 12px; border-radius: 12px;
  background: rgba(240,171,78,0.08); border: 1px solid rgba(240,171,78,0.25);
}
.pt-tune-up-preview-label {
  margin: 0 0 4px; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--pt-ink-faint);
}
.pt-tune-up-preview-text { margin: 0 0 8px; font-size: 13px; line-height: 1.4; white-space: pre-wrap; }
.pt-tune-up-preview-actions { display: flex; gap: 8px; }
.pt-tune-up-use-btn, .pt-tune-up-keep-btn {
  font-size: 12px; font-weight: 700; padding: 7px 14px; border-radius: 100px;
  min-height: var(--pt-tap);
}
.pt-tune-up-use-btn {
  background: var(--pt-accent-strong); border: 1px solid var(--pt-accent-strong); color: #1a1206;
}
.pt-tune-up-keep-btn {
  background: transparent; border: 1px solid rgba(240,171,78,0.35); color: var(--pt-accent-strong);
}

/* #heroCard (the old mascot-video card): every functional child it ever
   had (player, generateBtn, downloadBtn, Copy Lyrics, language picker,
   video buttons, Lab diagnostics) is re-homed elsewhere by screens.js;
   its own video content is already retired site-wide (.hero-video-frame
   is display:none, below). What's left is a genuinely empty shell that
   only ever rendered as a stray floating box in the main creation
   canvas - hidden outright rather than left to render nothing useful. */
#heroCard { display: none; }

/* Generating screen: honest indeterminate stage copy (no fabricated
   percentage - see app.js's GEN_STAGES) plus a livelier bloom animation
   in place of the previous almost-static pulse. */
.pt-gen-stage-cycle {
  margin: 2px 0 0; font: 600 14px var(--pt-sans); color: var(--pt-accent-strong);
  min-height: 18px;
}
/* Honest indeterminate activity bar - a sliding highlight, never a
   percentage, since no provider exposes real generation progress. Purely
   decorative alongside the real #status text and the stage-cycle copy. */
.pt-gen-activity {
  margin-top: 8px; height: 4px; border-radius: 100px; overflow: hidden;
  background: rgba(255,255,255,0.08);
}
.pt-gen-activity span {
  display: block; height: 100%; width: 40%; border-radius: 100px;
  background: linear-gradient(90deg, transparent, var(--pt-accent), transparent);
  animation: ptActivitySlide 1.6s ease-in-out infinite;
}
@keyframes ptActivitySlide {
  0% { transform: translateX(-110%); }
  100% { transform: translateX(280%); }
}
@media (prefers-reduced-motion: reduce) {
  .pt-gen-activity span { animation: none; opacity: .6; }
}

/* Account modal: billing content re-homed from the main page flow (see
   screens.js) - same #billingContent node/collapse toggle, just a new
   parent. The now-empty outer card must never render as a leftover box. */
#billingCard { display: none; }
#accountModalBillingHost:empty { display: none; }
#accountModalBillingHost { margin: 0 0 10px; }
#accountModalBillingHost .card-frame { padding: 0; }
.modal-panel .pt-account-section {
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 12px;
  padding: 14px;
  background: rgba(255,255,255,.045);
}
.modal-panel .pt-account-section .section-header { margin-bottom: 8px; }
.modal-panel .pt-account-section .section-header h2 { margin: 0; }

/* Result screen: language picker re-homed next to Copy Lyrics (see
   screens.js) - was previously stranded inside the mascot card. */
#ptSongActions #lyricsLanguageSelect { margin-top: 6px; }

/* Bottom nav: 5 icon+label tabs (Create / My Songs / Videos / Activity /
   Account) instead of 4 icon-only circles - same buttons/listeners,
   restyled to fit a text label under each icon. */
.tab-bar { border-radius: 22px; padding: 6px 4px; justify-content: space-around; }
.tab {
  width: auto; min-width: 56px; height: 56px; border-radius: 14px;
  flex-direction: column; gap: 3px; padding: 6px 4px;
}
.tab-label { font-size: 9.5px; font-weight: 600; letter-spacing: .01em; }
.tab.active { border-radius: 14px; }

/* ---------- PT-20260912-001: permanent Home/resting page ----------
   Home lives in the normal page flow below the global scenic header. It
   is not a modal or takeover, so the sunset and every navigation control
   remain visible. The existing creation workspaces still do the work;
   this section is only their cleaner entry and status surface. */
.pt-welcome-screen {
  display: none;
  position: relative;
  z-index: 2;
  width: 100%;
  padding: 0 max(18px, var(--pt-safe-r)) max(48px, var(--pt-safe-b)) max(18px, var(--pt-safe-l));
}
.pt-welcome-screen.open { display: block; }
/* The shorter home destinations must not expose the body theme below the
   scenic shell on tall screens. Grow the outer page, not the welcome card;
   retain natural scrolling when content is taller than the viewport. */
body.pt-home-open .page { min-height: 100vh; min-height: 100dvh; }
.pt-welcome-screen[hidden],
body.pt-creator-open .pt-welcome-screen { display: none !important; }
.pt-buddy-help-page {
  display: none;
  position: relative;
  z-index: 2;
  width: 100%;
  padding: 0 max(18px, var(--pt-safe-r)) max(48px, var(--pt-safe-b)) max(18px, var(--pt-safe-l));
}
.pt-buddy-help-page.open { display: block; }
.pt-buddy-help-page[hidden] { display: none !important; }
body.pt-home-open .pt-logo-hero,
body.pt-home-open .pt-arrival,
body.pt-home-open .pt-creation,
body.pt-home-open .pt-screen[data-screen-only],
body.pt-buddy-help-open .pt-logo-hero,
body.pt-buddy-help-open .pt-arrival,
body.pt-buddy-help-open .pt-creation,
body.pt-buddy-help-open .pt-welcome-screen,
body.pt-buddy-help-open .pt-screen[data-screen-only] { display: none !important; }
.pt-welcome-content {
  position: relative;
  width: min(100%, 980px);
  margin: -54px auto 0;
  background: linear-gradient(155deg, rgba(29,39,49,.98), rgba(20,27,35,.98));
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius-lg);
  box-shadow: var(--pt-shadow);
  padding: 30px;
}
.pt-buddy-help-content {
  position: relative;
  width: min(100%, 980px);
  margin: -54px auto 0;
  background: linear-gradient(155deg, rgba(29,39,49,.98), rgba(20,27,35,.98));
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius-lg);
  box-shadow: var(--pt-shadow);
  padding: 30px;
}
.pt-welcome-header {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr);
  align-items: center;
  gap: 26px;
  margin-bottom: 26px;
}
.pt-welcome-header > *,
.pt-welcome-header > div { min-width: 0; }
.pt-welcome-logo { width: 132px; height: auto; max-width: 100%; }
.pt-welcome-eyebrow {
  margin: 0 0 7px;
  color: var(--pt-accent-strong);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
}
.pt-welcome-heading {
  font-family: var(--pt-serif); font-size: 30px; line-height: 1.15;
  margin: 0 0 8px; color: var(--pt-ink);
  overflow-wrap: anywhere;
}
.pt-welcome-sub {
  font-family: var(--pt-sans); font-size: 15px; line-height: 1.5;
  color: var(--pt-ink-soft); margin: 0; max-width: 60ch;
}
.pt-welcome-cards {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: var(--pt-gap);
}
.pt-welcome-card {
  position: relative; text-align: left;
  display: flex; flex-direction: column; gap: 8px;
  padding: 22px 20px;
  background: var(--pt-surface-2);
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
  color: var(--pt-ink);
  cursor: pointer;
  transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}
.pt-welcome-card:hover, .pt-welcome-card:focus-visible {
  transform: translateY(-3px);
  border-color: var(--pt-accent);
  box-shadow: var(--pt-shadow-soft);
}
.pt-welcome-card-icon { font-size: 26px; line-height: 1; }
.pt-welcome-card-title {
  font-family: var(--pt-sans); font-weight: 700; font-size: 16px; color: var(--pt-ink);
}
.pt-welcome-card-desc {
  font-family: var(--pt-sans); font-size: 13.5px; color: var(--pt-ink-soft); line-height: 1.4;
}
.pt-home-learn-more {
  grid-column: auto;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  min-height: 72px;
  padding: 14px 18px;
  text-align: left;
  color: var(--pt-ink);
  background: var(--pt-surface-2);
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
  cursor: pointer;
  transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
.pt-home-learn-more:hover, .pt-home-learn-more:focus-visible {
  transform: translateY(-2px);
  border-color: var(--pt-accent-strong);
  box-shadow: var(--pt-shadow-soft);
}
.pt-home-learn-more-art { font-size: 25px; line-height: 1; }
.pt-home-learn-more-copy { display: flex; flex-direction: column; gap: 3px; }
.pt-home-learn-more-title { font: 700 15px/1.3 var(--pt-sans); }
.pt-home-learn-more-desc { color: var(--pt-ink-soft); font: 500 12.5px/1.4 var(--pt-sans); }
.pt-home-learn-more-arrow { color: var(--pt-accent-strong); font-size: 24px; }
.pt-home-trust {
  margin-top: 24px;
  padding: 28px clamp(18px, 3vw, 34px);
  border-top: 1px solid var(--pt-line);
  border-bottom: 1px solid rgba(240,171,78,.18);
  background:
    radial-gradient(circle at 50% 0%, rgba(240,171,78,.1), transparent 38%),
    linear-gradient(180deg, rgba(255,255,255,.018), rgba(255,255,255,0));
  border-radius: var(--pt-radius);
}
.pt-home-trust-summary {
  position: relative;
  display: block;
  cursor: pointer;
  list-style: none;
  padding-right: 118px;
}
.pt-home-trust-summary::-webkit-details-marker { display: none; }
.pt-home-trust-summary:focus-visible {
  outline: 3px solid var(--pt-accent-strong);
  outline-offset: 8px;
  border-radius: 8px;
}
.pt-home-trust-toggle {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  min-width: 92px;
  padding: 9px 12px;
  border: 1px solid rgba(255,255,255,.18);
  border-radius: 999px;
  color: var(--pt-ink);
  font: 800 12px/1 var(--pt-sans);
  text-align: center;
}
.pt-home-trust[open] .pt-home-trust-toggle::before { content: "Hide details"; }
.pt-home-trust[open] .pt-home-trust-toggle { font-size: 0; }
.pt-home-trust[open] .pt-home-trust-toggle::before { font-size: 12px; }
.pt-home-trust-content { margin-top: 24px; }
.pt-home-trust-heading {
  max-width: 760px;
  margin: 0 auto 22px;
  text-align: center;
}
.pt-home-trust-heading h2 {
  margin: 0;
  color: var(--pt-ink);
  font: 700 clamp(24px, 3vw, 34px)/1.15 var(--pt-serif);
}
.pt-home-trust-intro {
  max-width: 650px;
  margin: 10px auto 0;
  color: var(--pt-ink-soft);
  font: 500 14px/1.6 var(--pt-sans);
}
.pt-home-trust-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
.pt-home-trust-card {
  display: flex;
  min-width: 0;
  flex-direction: column;
  padding: 22px;
  background: linear-gradient(155deg, rgba(240,171,78,.09), rgba(255,255,255,.025) 58%);
  border: 1px solid rgba(240,171,78,.28);
  border-radius: var(--pt-radius);
  box-shadow: 0 16px 38px rgba(0,0,0,.12);
}
.pt-home-trust-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}
.pt-home-trust-icon {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  color: var(--pt-accent-strong);
  background: rgba(240,171,78,.12);
  border: 1px solid rgba(240,171,78,.34);
  border-radius: 14px;
  font: 800 20px/1 var(--pt-sans);
}
.pt-home-trust-badge {
  display: inline-block;
  padding: 5px 10px;
  color: var(--pt-cta-ink);
  background: linear-gradient(110deg, var(--pt-accent-strong), #ffd08a);
  border-radius: 999px;
  font: 800 10px/1.2 var(--pt-sans);
  letter-spacing: .07em;
  text-transform: uppercase;
}
.pt-home-trust-card h3 { margin: 0 0 9px; color: var(--pt-ink); font: 750 18px/1.3 var(--pt-sans); }
.pt-home-trust-card p { margin: 0; color: var(--pt-ink-soft); font: 500 13px/1.55 var(--pt-sans); }
.pt-home-trust-points { display: grid; gap: 7px; margin: 15px 0 18px; padding: 0; list-style: none; }
.pt-home-trust-points li { position: relative; padding-left: 20px; color: rgba(255,255,255,.82); font: 600 12px/1.45 var(--pt-sans); }
.pt-home-trust-points li::before { content: "✓"; position: absolute; left: 0; color: var(--pt-accent-strong); font-weight: 900; }
.pt-home-trust-card .pt-home-trust-note { margin-top: auto; padding-top: 13px; border-top: 1px solid rgba(255,255,255,.09); color: rgba(255,255,255,.66); font-size: 11.5px; }
.pt-home-trust-note strong { color: var(--pt-ink); }
.pt-home-trust-card-buddy { background: linear-gradient(145deg, rgba(224,108,56,.12), rgba(102,132,174,.08)); }
.pt-home-trust-links { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px 18px; margin: 18px 0 0; padding-top: 16px; border-top: 1px solid rgba(255,255,255,.07); color: var(--pt-ink-soft); font: 600 12px/1.5 var(--pt-sans); }
.pt-home-trust-links span { color: var(--pt-ink); font-weight: 800; }
.pt-home-trust-links a { color: var(--pt-accent-strong); text-decoration-thickness: 1px; text-underline-offset: 3px; }
/* Recommended path: visually prominent via a gold-to-copper border and
   a small badge - never dims or disables the other three, which keep
   the exact same size, contrast, and interactivity. */
.pt-welcome-card-featured {
  border-color: var(--pt-accent);
  background: linear-gradient(165deg, rgba(240,171,78,0.14) 0%, var(--pt-surface-2) 55%);
}
.pt-welcome-card-featured:hover, .pt-welcome-card-featured:focus-visible {
  border-color: var(--pt-accent-strong);
  box-shadow: 0 10px 26px rgba(240,171,78,0.22);
}
.pt-welcome-card-badge {
  align-self: flex-start;
  font-family: var(--pt-sans); font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--pt-cta-ink); background: var(--pt-accent-strong);
  padding: 3px 9px; border-radius: 999px;
}
.pt-home-progress,
.pt-home-ready,
.pt-home-error {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 24px;
  padding: 22px;
  margin-bottom: 22px;
  background: rgba(255,255,255,.035);
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
}
.pt-home-progress[hidden], .pt-home-ready[hidden], .pt-home-error[hidden],
.pt-welcome-cards[hidden] { display: none; }
.pt-home-progress-buddy { display: block; width: 112px; height: auto; }
.pt-home-progress h2, .pt-home-ready h2, .pt-home-error h2 {
  font-family: var(--pt-serif);
  margin: 0 0 8px;
  font-size: clamp(23px, 3vw, 32px);
}
.pt-home-progress-note, .pt-home-ready p, .pt-home-error p {
  margin: 8px 0 0;
  color: var(--pt-ink-soft);
  line-height: 1.45;
}
.pt-home-buddy-rest {
  margin: 10px 0 0;
  color: var(--pt-accent-strong);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.4;
}
.pt-home-ready { grid-template-columns: minmax(0, 1fr) auto; border-color: rgba(240,171,78,.45); }
.pt-home-error { grid-template-columns: minmax(0, 1fr) auto; border-color: rgba(220,119,78,.55); }
.pt-home-ready-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
.pt-home-ready-actions button { min-height: var(--pt-tap); }
.pt-home-progress-library { margin-top: 14px; }
.pt-song-status-bar[hidden] { display: none; }
.pt-song-status-bar {
  position: fixed; z-index: 10010; left: 50%; transform: translateX(-50%);
  bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  width: min(620px, calc(100% - 24px)); box-sizing: border-box;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 16px; border-radius: 16px; border: 1px solid var(--pt-line);
  background: var(--pt-surface, #fff); color: var(--pt-ink, #18233d);
  box-shadow: 0 8px 24px rgba(0,0,0,.18);
}
.pt-song-status-bar > div { min-width: 0; display: grid; gap: 3px; }
.pt-song-status-bar > div { flex: 1; }
.pt-song-progress-meter { display: block; width: 100%; height: 12px; accent-color: var(--pt-accent, #b66b3c); }
body.pt-has-song-status #ptHomeStageHost,
body.pt-has-song-status #ptHomeStatusHost { display: none; }
.pt-song-status-bar strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pt-song-status-bar span { font-size: 13px; line-height: 1.4; }
.pt-song-status-bar button { flex-shrink: 0; min-height: 44px; }
body.pt-has-song-status { padding-bottom: calc(110px + var(--pt-song-status-height, 90px) + env(safe-area-inset-bottom, 0px)); }
body.pt-has-song-status .pt-library-screen { padding-bottom: calc(110px + var(--pt-song-status-height, 90px) + env(safe-area-inset-bottom, 0px)); }
body.pt-has-song-status .modal-backdrop > .modal-panel { max-height: calc(100dvh - 180px - var(--pt-song-status-height, 90px)); }
body.pt-has-song-status #settingsModal > .modal-panel { max-height: calc(100dvh - 120px - var(--pt-song-status-height, 90px)) !important; }
/* Stage copy and actual status must occupy distinct lines, not overlap. */
#ptHomeStageHost, #ptHomeStatusHost { position: relative; display: block; min-height: 1.6em; }
#ptHomeStageHost > *, #ptHomeStatusHost > * { position: static; transform: none; }
@media (max-width: 390px) {
  .pt-song-status-bar { gap: 8px; padding: 10px; }
  .pt-song-status-bar button { max-width: 108px; padding: 8px; }
}
.pt-buddy-news {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr);
  align-items: center;
  gap: 18px;
  margin-bottom: 18px;
  padding: 22px 24px;
  border: 1px solid rgba(255,185,92,.58);
  border-radius: var(--pt-radius);
  background:
    radial-gradient(circle at 92% 0%, rgba(255,194,104,.24), transparent 34%),
    linear-gradient(115deg, rgba(166,68,31,.42), rgba(240,171,78,.13) 48%, rgba(255,255,255,.035));
  box-shadow: 0 14px 34px rgba(0,0,0,.2), inset 0 1px rgba(255,255,255,.08);
}
.pt-buddy-news::after {
  content: "";
  position: absolute;
  z-index: -1;
  width: 180px;
  height: 180px;
  right: -68px;
  bottom: -115px;
  border: 1px solid rgba(255,210,141,.2);
  border-radius: 50%;
  box-shadow: 0 0 42px rgba(240,171,78,.12);
}
.pt-buddy-news-icon {
  display: grid;
  place-items: center;
  width: 58px;
  height: 58px;
  border: 1px solid rgba(255,210,141,.56);
  border-radius: 18px;
  color: #ffe1ae;
  background: linear-gradient(145deg, rgba(255,190,91,.3), rgba(119,43,18,.36));
  box-shadow: 0 8px 24px rgba(0,0,0,.22), inset 0 1px rgba(255,255,255,.13);
  font-size: 28px;
}
.pt-buddy-news-kicker {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 7px;
  color: #ffd08a;
  font: 800 11px/1.2 var(--pt-sans);
  letter-spacing: .14em;
  text-transform: uppercase;
}
.pt-buddy-news-kicker strong {
  padding: 3px 7px;
  border-radius: 999px;
  color: #2b160c;
  background: linear-gradient(135deg, #ffd27e, #ef8f42);
  font-size: 9px;
  letter-spacing: .08em;
  box-shadow: 0 4px 12px rgba(240,143,66,.25);
}
.pt-buddy-news h2 { margin: 0 0 6px; font: 700 clamp(21px, 3vw, 27px)/1.2 var(--pt-serif); }
.pt-buddy-news p:last-child { margin: 0; color: var(--pt-ink-soft); line-height: 1.5; }
.pt-buddy-help-landing,
.pt-buddy-subpage,
.pt-buddy-studio { display: grid; gap: 18px; }
.pt-buddy-help-landing[hidden],
.pt-buddy-subpage[hidden] { display: none !important; }
.pt-buddy-destinations {
  padding: 22px;
  border: 1px solid rgba(133,159,187,.28);
  border-radius: var(--pt-radius);
  background: linear-gradient(150deg, rgba(255,255,255,.04), rgba(255,255,255,.015));
}
.pt-buddy-destinations-heading { margin-bottom: 15px; text-align: center; }
.pt-buddy-destinations-heading h2 {
  margin: 0;
  color: var(--pt-ink);
  font: 700 clamp(22px, 3vw, 29px)/1.2 var(--pt-serif);
}
.pt-buddy-destination-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}
.pt-buddy-destination {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  min-width: 0;
  min-height: 150px;
  padding: 20px;
  text-align: left;
  color: var(--pt-ink);
  border: 1px solid var(--pt-line);
  border-radius: 16px;
  background: linear-gradient(145deg, rgba(255,255,255,.055), rgba(255,255,255,.02));
  cursor: pointer;
  transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
.pt-buddy-destination:hover,
.pt-buddy-destination:focus-visible {
  transform: translateY(-3px);
  border-color: var(--pt-accent-strong);
  box-shadow: 0 14px 30px rgba(0,0,0,.22);
}
.pt-buddy-destination-coming-soon,
.pt-buddy-destination-coming-soon:disabled {
  cursor:default;
  opacity:.82;
  border-style:dashed;
  background:linear-gradient(145deg, color-mix(in srgb,var(--pt-surface-2) 76%,transparent), color-mix(in srgb,var(--pt-surface) 88%,transparent));
}
.pt-buddy-destination-coming-soon:hover,
.pt-buddy-destination-coming-soon:focus-visible { transform:none; box-shadow:none; border-color:var(--pt-line); }
.pt-buddy-destination-status {
  justify-self:end;
  padding:6px 9px;
  border:1px solid var(--pt-line);
  border-radius:999px;
  color:var(--pt-muted);
  background:var(--pt-surface);
  font:800 10px/1 var(--pt-sans);
  letter-spacing:.06em;
  text-transform:uppercase;
}
.pt-buddy-destination-featured {
  border-color: rgba(240,171,78,.5);
  background:
    radial-gradient(circle at 100% 0%, rgba(232,126,61,.18), transparent 42%),
    linear-gradient(145deg, rgba(240,171,78,.1), rgba(255,255,255,.02));
}
.pt-buddy-destination-icon {
  display: grid;
  width: 48px;
  height: 48px;
  place-items: center;
  border: 1px solid rgba(240,171,78,.35);
  border-radius: 15px;
  background: rgba(240,171,78,.1);
  font-size: 24px;
}
.pt-buddy-destination-copy { display: grid; gap: 5px; min-width: 0; }
.pt-buddy-destination-kicker {
  color: var(--pt-accent-strong);
  font: 800 10px/1.2 var(--pt-sans);
  letter-spacing: .12em;
  text-transform: uppercase;
}
.pt-buddy-destination-copy strong { font: 700 20px/1.2 var(--pt-serif); }
.pt-buddy-destination-copy > span:last-child { color: var(--pt-ink-soft); font: 500 12.5px/1.45 var(--pt-sans); }
.pt-buddy-destination-arrow { color: var(--pt-accent-strong); font-size: 25px; }
.pt-buddy-subpage-back {
  justify-self: start;
  min-height: var(--pt-tap);
  padding: 9px 15px;
  border: 1px solid var(--pt-line);
  border-radius: 999px;
  color: var(--pt-ink);
  background: rgba(255,255,255,.035);
  font: 700 13px var(--pt-sans);
}
.pt-buddy-subpage-back:hover,
.pt-buddy-subpage-back:focus-visible { border-color: var(--pt-accent); }
.pt-home-buddy-guide {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  align-items: start;
  gap: 18px;
  margin-top: 0;
  padding: 0;
}
.pt-home-buddy-guide-art {
  display: block;
  width: 128px;
  height: auto;
  padding: 12px 14px 0;
  border: 1px solid rgba(240,171,78,.34);
  border-radius: 24px;
  background:
    radial-gradient(circle at 50% 22%, rgba(240,171,78,.2), transparent 48%),
    rgba(255,255,255,.025);
  box-shadow: var(--pt-shadow-soft);
}
.pt-buddy-studio .pt-home-buddy-interactive,
.pt-buddy-studio .pt-home-buddy-workspace { margin: 0; }
.pt-home-buddy-guide-main { display: grid; gap: 14px; min-width: 0; }
.pt-home-buddy-quick,
.pt-home-buddy-interactive,
.pt-home-buddy-workspace {
  padding: 20px;
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
  background: linear-gradient(150deg, rgba(255,255,255,.045), rgba(255,255,255,.018));
  box-shadow: 0 10px 28px rgba(0,0,0,.14);
}
.pt-home-buddy-quick { border-color: rgba(240,171,78,.3); }
.pt-home-buddy-guide h2 { margin: 0 0 6px; font: 700 clamp(20px, 3vw, 27px)/1.2 var(--pt-serif); }
.pt-home-buddy-context { margin: 0 0 14px; color: var(--pt-ink-soft); line-height: 1.45; }
.pt-home-buddy-topics { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.pt-home-buddy-topic {
  min-height: 36px;
  padding: 7px 11px;
  border: 1px solid var(--pt-line);
  border-radius: 999px;
  background: rgba(255,255,255,.035);
  color: var(--pt-ink-soft);
  font: 600 12px var(--pt-sans);
}
.pt-home-buddy-topic:hover, .pt-home-buddy-topic:focus-visible { border-color: var(--pt-accent); color: var(--pt-ink); }
.pt-home-buddy-topic[aria-pressed="true"] {
  border-color: var(--pt-accent-strong);
  background: rgba(240,171,78,.14);
  color: var(--pt-ink);
}
.pt-saved-ideas .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.pt-home-buddy-answer {
  margin: 13px 0 0;
  padding: 13px 15px;
  border-left: 3px solid var(--pt-accent-strong);
  border-radius: 0 10px 10px 0;
  background: rgba(255,255,255,.035);
  color: var(--pt-ink);
  line-height: 1.5;
}
.pt-home-buddy-answer strong { display: block; margin-bottom: 5px; color: var(--pt-accent-soft); }
.pt-home-buddy-answer[hidden] { display: none; }
.pt-home-buddy-boundary { margin: 10px 0 0; color: var(--pt-ink-faint); font-size: 11px; line-height: 1.4; }
.pt-home-buddy-interactive,
.pt-home-buddy-workspace {
  margin-top: 0;
}
.pt-home-buddy-interactive {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  border-color: rgba(232,126,61,.44);
  background:
    radial-gradient(circle at 100% 50%, rgba(232,126,61,.13), transparent 32%),
    linear-gradient(150deg, rgba(240,171,78,.07), rgba(255,255,255,.02));
}
.pt-home-buddy-interactive h3,
.pt-home-buddy-workspace h3 { margin: 0 0 5px; font: 700 20px/1.25 var(--pt-serif); }
.pt-home-buddy-interactive p:not(.pt-welcome-eyebrow) { margin: 0; color: var(--pt-ink-soft); line-height: 1.5; }
.pt-home-buddy-workspace-heading {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.pt-home-buddy-workspace { border-color: rgba(133,159,187,.28); }
.pt-home-buddy-workspace > .pt-home-buddy-interactive {
  margin:0 0 10px;
  padding:12px;
  border-width:1px;
  border-radius:12px;
  box-shadow:none;
}
.pt-home-buddy-workspace select,
.pt-home-buddy-workspace textarea {
  border: 1px solid var(--pt-line);
  border-radius: 12px;
  background: var(--pt-surface-2);
  color: var(--pt-ink);
  font: 500 14px/1.5 var(--pt-sans);
}
.pt-home-buddy-workspace select { min-height: var(--pt-tap); padding: 8px 12px; }
.pt-home-buddy-workspace textarea { display: block; width: 100%; min-height: 150px; padding: 13px; resize: vertical; }
.pt-buddy-workspace-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px;
  margin-top: 9px;
}
.pt-buddy-workspace-tools .pt-porch-sauce,
.pt-buddy-workspace-tools .pt-tune-up { margin-top: 0; }
.pt-buddy-workspace-tools .pt-porch-sauce-preview,
.pt-buddy-workspace-tools .pt-tune-up-panel,
.pt-buddy-workspace-tools .pt-tune-up-preview { flex-basis: 100%; }
.pt-home-buddy-workspace-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 9px;
  color: var(--pt-ink-faint);
  font-size: 11px;
}
.pt-saved-ideas {
  margin-top: 18px;
  padding-top: 17px;
  border-top: 1px solid var(--pt-line);
}
.pt-saved-ideas-heading {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.pt-saved-ideas-heading h4 { margin: 0; font: 700 17px/1.25 var(--pt-serif); }
.pt-saved-ideas-heading > span { color: var(--pt-ink-faint); font-size: 11px; }
.pt-saved-ideas-save,
.pt-saved-ideas-load {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 9px;
}
.pt-saved-ideas-load { grid-template-columns: minmax(0, 1fr) auto auto; margin-top: 9px; }
.pt-saved-ideas input,
.pt-saved-ideas select {
  min-width: 0;
  min-height: var(--pt-tap);
  padding: 9px 12px;
  border: 1px solid var(--pt-line);
  border-radius: 12px;
  background: var(--pt-surface-2);
  color: var(--pt-ink);
  font: 500 13px var(--pt-sans);
}
.pt-saved-ideas-status { min-height: 1.3em; margin: 8px 0 0; color: var(--pt-ink-soft); font-size: 12px; }
.pt-saved-idea-delete { color: #ffb2a5; }
.pt-home-buddy-voice-host:not(:empty) {
  margin-top: 16px;
  padding: 14px;
  border: 1px solid rgba(240,171,78,.28);
  border-radius: 14px;
  background: rgba(0,0,0,.12);
}
.pt-home-buddy-voice-host .pt-buddy-dock {
  position: static !important;
  inset: auto !important;
  width: 100%;
  max-width: none;
  transform: none !important;
  z-index: auto;
}
.pt-home-buddy-voice-host #pbVoicePanels,
.pt-home-buddy-voice-host #pbVoiceWrap { max-width: none !important; }

/* The focused creator is the second state of the same visual system as
   Home. Existing certified form controls remain in place inside it. */
.pt-creator-header {
  display: grid;
  grid-template-columns: auto 96px minmax(0, 1fr);
  align-items: center;
  gap: 18px;
  margin-bottom: 22px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--pt-line);
}
.pt-creator-logo {
  display: block;
  width: 96px;
  max-width: 96px;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
}
.pt-creator-back {
  min-height: var(--pt-tap);
  padding: 0 16px;
  border: 1px solid var(--pt-line);
  border-radius: 999px;
  background: rgba(255,255,255,.035);
  color: var(--pt-ink);
}
.pt-creator-title { margin: 0 0 7px; font: 700 clamp(26px, 4vw, 38px)/1.12 var(--pt-serif); }
.pt-creator-sub { margin: 0; color: var(--pt-ink-soft); line-height: 1.5; }
body.pt-creator-open .pt-arrival {
  display: block !important;
  position: relative;
  z-index: 2;
  width: min(calc(100% - 36px), 980px);
  margin: -54px auto 48px;
  padding: 30px 36px 38px;
  background: linear-gradient(155deg, rgba(29,39,49,.98), rgba(20,27,35,.98));
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius-lg);
  box-shadow: var(--pt-shadow);
}
body.pt-creator-open .pt-arrival-lockup,
body.pt-creator-open .pt-create-manually-toggle,
body.pt-creator-open .pt-voice-duration,
body.pt-creator-open .pt-customize-toggle { display: none !important; }
/* Unified Creator: the four Home choices open one shared workspace with
   a persistent mode selector. Existing live inputs and listeners stay in
   place; this grid changes only their presentation. */
body.pt-creator-open .pt-arrival {
  display: grid !important;
  grid-template-columns: minmax(0, 1fr) 270px;
  grid-template-areas:
    "header header"
    "modes modes"
    "prompt rail"
    "commit commit"
    "cost cost";
  gap: 18px;
  align-items: start;
}
body.pt-creator-open .pt-creator-header { grid-area: header; }
body.pt-creator-open .pt-starting-point {
  grid-area: modes;
  display: block;
  margin: 0;
}
body.pt-creator-open #startingModeRow {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin: 0;
}
body.pt-creator-open #startingModeRow .pt-mode-chip {
  min-width: 0;
  min-height: 58px;
  padding: 10px 12px;
  border-radius: 14px;
  justify-content: center;
}
.pt-creator-howto {
  margin-top: 14px;
  padding: 16px;
  border: 1px solid rgba(240,171,78,.24);
  border-radius: 14px;
  background: linear-gradient(110deg, rgba(240,171,78,.09), rgba(130,161,210,.07));
  color: var(--pt-ink-soft);
  font: 600 12px/1.45 var(--pt-sans);
}
.pt-creator-howto-heading {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 13px;
}
.pt-creator-howto-icon {
  display: grid;
  flex: 0 0 36px;
  width: 36px;
  height: 36px;
  place-items: center;
  border: 1px solid rgba(240,171,78,.42);
  border-radius: 12px;
  background: rgba(240,171,78,.13);
  color: var(--pt-accent-strong);
  font-size: 18px;
}
.pt-creator-howto-heading div {
  display: grid;
  gap: 1px;
}
.pt-creator-howto-heading strong {
  color: var(--pt-accent-strong);
  font-weight: 850;
  font-size: 13px;
}
.pt-creator-howto-heading span:last-child {
  color: var(--pt-ink-muted);
  font-size: 11px;
}
.pt-creator-howto ol {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pt-creator-howto li {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  align-items: start;
  gap: 9px;
  min-width: 0;
  min-height: 76px;
  padding: 12px;
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 12px;
  background: rgba(10,18,28,.28);
}
.pt-creator-step-number {
  display: grid;
  width: 28px;
  height: 28px;
  place-items: center;
  border-radius: 50%;
  background: linear-gradient(145deg, #f4b254, #cf6736);
  color: #101824;
  font-weight: 900;
  line-height: 1;
}
body.pt-creator-open .pt-prompt {
  grid-area: prompt;
  min-width: 0;
}
body.pt-creator-open .pt-creator-rail {
  grid-area: rail;
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 18px;
}
body.pt-creator-open .pt-quality {
  margin: 0;
  padding: 18px;
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-radius);
  background: var(--pt-surface);
}
body.pt-creator-open .pt-commit { grid-area: commit; }
body.pt-creator-open .pt-cta-cost { grid-area: cost; }
body.pt-creator-open:not([data-pt-creator-mode="lyrics"]) #detailsCard {
  min-height: 0;
}
body.pt-creator-open:not([data-pt-creator-mode="lyrics"]) #detailsCard #details {
  min-height: 210px;
}
.pt-quality-help {
  margin: -3px 0 12px;
  color: var(--pt-ink-faint);
  font: 500 12px var(--pt-sans);
}
body.pt-creator-open #qualityTierRow {
  display: grid;
  grid-template-columns: 1fr;
  gap: 9px;
  margin: 0;
}
body.pt-creator-open .pt-quality-choice {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 66px;
  padding: 10px 12px;
  text-align: left;
  border-radius: 14px;
}
.pt-quality-radio {
  display: grid;
  grid-column: 1;
  grid-row: 1;
  place-items: center;
  width: 18px;
  height: 18px;
  border: 2px solid var(--pt-ink-faint);
  border-radius: 50%;
}
.pt-quality-choice.active .pt-quality-radio { border-color: var(--pt-accent-strong); }
.pt-quality-choice.active .pt-quality-radio::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--pt-accent-strong);
}
.pt-quality-copy { display: flex; grid-column: 2; grid-row: 1; flex-direction: column; min-width: 0; }
.pt-quality-name { color: var(--pt-ink); font-weight: 800; }
.pt-quality-limit { color: var(--pt-ink-faint); font-size: 11px; font-weight: 500; }
.pt-quality-credits { grid-column: 3; grid-row: 1; color: var(--pt-accent-strong); font-size: 12px; font-weight: 800; white-space: nowrap; }
.pt-buddy-jump-lyrics {
  display: none;
  align-self: center;
  min-height: var(--pt-tap);
  margin: 12px auto 0;
  padding: 10px 16px;
  border: 1px solid rgba(255,193,92,.48);
  border-radius: 999px;
  background: rgba(255,177,74,.1);
  color: #ffd18a;
  font: 800 14px var(--pt-sans);
  text-decoration: none;
  box-shadow: 0 6px 20px rgba(0,0,0,.16);
}
.pt-buddy-jump-lyrics:hover,
.pt-buddy-jump-lyrics:focus-visible {
  border-color: #ffd18a;
  background: rgba(255,177,74,.18);
  transform: translateY(-1px);
}
body.pt-creator-open[data-pt-creator-mode="buddy"] .pt-buddy-jump-lyrics { display: block; }
body.pt-creator-open[data-pt-creator-mode="buddy"] .pt-creator-header { margin-bottom: 8px; }
body.pt-creator-open .pt-arrival-buddy-stage {
  display: flex !important;
  min-height: 210px;
  justify-content: center;
  padding: 8px 12px 2px;
}
body.pt-creator-open #ptArrivalBuddyStart { display: none !important; }
.pt-creator-buddy-presence {
  display: none;
  width: 100%;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  text-align: center;
}
body.pt-creator-open .pt-creator-buddy-presence { display: flex; }
.pt-creator-buddy-character {
  display: block;
  width: 132px;
  max-width: 58%;
  height: auto;
  object-fit: contain;
  filter: drop-shadow(0 14px 20px rgba(0,0,0,.28));
  animation: pbFloat 3.6s ease-in-out infinite;
}
.pt-creator-buddy-presence p {
  max-width: 230px;
  margin: 2px auto 0;
  color: var(--pt-ink-soft);
  font: 650 12px/1.45 var(--pt-sans);
}
body.pt-home-open .pt-buddy-dock,
body.pt-creator-open:not([data-pt-creator-mode="buddy"]) .pt-buddy-dock { display: none !important; }
/* [PT-20260926-001] Porch Sauce is no longer hidden in the Porch Buddy
   workspace.

   PT-20260912-001 kept both writing helpers out of Buddy's conversational
   experience. For Tune-Up that still holds - revising supplied lyrics is
   something Buddy does by talking. Porch Sauce is different: it improves
   the Song Description, and the Song Description field is right there in
   the Buddy workspace, editable. Hiding it meant the control was invisible
   on the journey Home actually recommends, which is how it came to be
   reported as missing from the description area entirely.

   It stays attached to the field it improves, wherever that field is. */
body.pt-creator-open[data-pt-creator-mode="buddy"] .pt-tune-up { display: none !important; }
.pt-buddy-field-assist {
  display: none;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid rgba(255,255,255,.09);
}
body.pt-creator-open .pt-buddy-field-assist { display: block; }
.pt-buddy-field-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: var(--pt-tap);
  padding: 11px 16px;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border: 1px solid rgba(255,200,103,.82);
  border-radius: 999px;
  background: linear-gradient(105deg, #ed632d, #ffbd4d, #8aa8dc, #9a62ca, #ed632d);
  background-size: 320% 100%;
  color: #111820;
  font: 750 14px/1.25 var(--pt-sans);
  box-shadow: 0 0 0 1px rgba(255,188,85,.14), 0 8px 28px rgba(232,126,61,.28);
  animation: pt-buddy-button-flow 6s ease-in-out infinite;
}
.pt-buddy-field-toggle:hover,
.pt-buddy-field-toggle:focus-visible { border-color: #fff1c6; transform: translateY(-1px); box-shadow: 0 0 0 3px rgba(255,193,92,.2), 0 12px 34px rgba(232,126,61,.38); }
.pt-buddy-field-toggle::after,
#ptHomeBuddyTalkBtn::after,
#ptHomeBuddyEscalate::after,
#pbVoiceResultTriggerBtn::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -90% auto -90% -30%;
  width: 22%;
  transform: rotate(18deg);
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.52), transparent);
  animation: pt-buddy-button-shine 3.8s ease-in-out infinite;
}
.pt-buddy-field-toggle[aria-pressed="true"] {
  border-color: var(--pt-accent-strong);
  background: linear-gradient(135deg, var(--pt-accent), var(--pt-copper));
  color: #111820;
  box-shadow: 0 8px 22px rgba(232,126,61,.22);
}
#ptHomeBuddyTalkBtn,
#ptHomeBuddyEscalate,
#pbVoiceResultTriggerBtn {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-color: rgba(255,200,103,.82);
  background: linear-gradient(105deg, #ed632d, #ffbd4d, #8aa8dc, #9a62ca, #ed632d);
  background-size: 320% 100%;
  color: #111820;
  font-weight: 850;
  box-shadow: 0 8px 28px rgba(232,126,61,.28);
  animation: pt-buddy-button-flow 6s ease-in-out infinite;
}
#ptHomeBuddyTalkBtn[aria-pressed="true"],
#ptHomeBuddyTalkBtn.is-active {
  border-color: #fff;
  background: linear-gradient(135deg, var(--pt-accent), var(--pt-copper));
  color: #111820;
  box-shadow: 0 0 0 3px rgba(255,255,255,.22), 0 10px 30px rgba(232,126,61,.35);
}
@keyframes pt-buddy-button-flow {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}
@keyframes pt-buddy-button-shine {
  0%, 55% { left: -35%; opacity: 0; }
  62% { opacity: 1; }
  82%, 100% { left: 120%; opacity: 0; }
}
.pt-buddy-field-status {
  margin: 7px 0 0;
  color: var(--pt-ink-faint);
  font: 500 11.5px/1.4 var(--pt-sans);
  text-align: center;
}
.pt-buddy-field-assist #pbVoicePanels {
  max-width: none !important;
  margin-top: 10px !important;
}
.pt-buddy-field-assist #pbVoiceConsent {
  padding: 14px !important;
  border-color: rgba(240,171,78,.3) !important;
  background: rgba(10,15,21,.28);
}
/* Creator voice is controlled by the button attached to the field being
   edited. The legacy floating dock remains available on its older result
   surface, but never competes with or obscures a creator field. */
body.pt-creator-open .pt-buddy-dock { display: none !important; }
body.pt-creator-open #detailsMicBtn { display: none !important; }
/* [PT-20260922-007] Inline first-use voice privacy disclosure. Sits
   quietly beneath each Talk control so it is read BEFORE the tap, in
   place of the removed blocking "Allow audio & start." panel. Hidden
   entirely once the current disclosure version is accepted. */
.pt-buddy-inline-disclosure {
  margin: 8px 0 0;
  padding: 10px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 10px;
  max-width: 360px;
  color: var(--pt-ink-soft);
  font-size: 13px;
  line-height: 1.5;
  text-align: left;
}
.pt-buddy-inline-disclosure p {
  margin: 0;
}

.pt-buddy-privacy-details {
  margin: 8px 0 10px;
  padding: 0;
}
.pt-buddy-privacy-details summary {
  min-height: 40px;
  display: flex;
  align-items: center;
  cursor: pointer;
  color: var(--pt-ink-soft);
  font-weight: 700;
}
.pt-buddy-privacy-details p {
  margin: 2px 0 10px !important;
  color: var(--pt-ink-faint);
  font-size: 12px;
}
.pt-library-timings {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 12px;
  color: var(--pt-ink-soft);
  font-size: 13px;
}
.pt-library-language {
  width: 100%;
  margin-top: 10px;
  border-radius: 999px;
  color: var(--pt-ink);
  background: rgba(12, 13, 20, .52);
  border: 1px solid var(--pt-line);
  padding: 10px 14px;
}
.pt-library-timings input { width: 18px; height: 18px; margin: 0; }
@media (min-width: 720px) {
  .pt-welcome-content { padding: 34px 38px 38px; }
  .pt-buddy-help-content { padding: 34px 38px 38px; }
  .pt-welcome-heading { font-size: 36px; }
}
@media (max-width: 719px) {
  .pt-library-primary-actions { gap:5px; }
  .pt-library-primary-actions button { padding:7px 5px; font-size:12px; }
  .pt-library-lyrics-tools { grid-template-columns:1fr; gap:7px; }
  .pt-welcome-cards { grid-template-columns: 1fr; }
  .pt-home-trust-grid { grid-template-columns: 1fr; }
  .pt-home-trust { margin-top: 20px; padding: 22px 14px; }
  .pt-home-trust-summary { padding-right: 0; padding-bottom: 50px; }
  .pt-home-trust-toggle { top: auto; right: auto; bottom: 0; left: 0; transform: none; }
  .pt-home-trust-heading { margin-bottom: 18px; }
  .pt-home-trust-card { padding: 18px; }
  .pt-home-trust-links { align-items: flex-start; flex-direction: column; }
  .pt-welcome-screen { padding-inline: 12px; }
  .pt-buddy-help-page { padding-inline: 12px; }
  .pt-welcome-content { margin-top: -22px; padding: 22px 16px 24px; border-radius: var(--pt-radius); }
  .pt-buddy-help-content { margin-top: -22px; padding: 20px 16px 24px; border-radius: var(--pt-radius); }
  .pt-welcome-header { grid-template-columns: 82px minmax(0, 1fr); gap: 14px; margin-bottom: 20px; }
  .pt-welcome-logo { width: 82px; }
  .pt-welcome-heading { font-size: 24px; }
  .pt-welcome-sub { font-size: 13.5px; }
  .pt-welcome-card { padding: 18px 16px; }
  .pt-home-learn-more { min-height: 68px; padding: 13px 14px; gap: 11px; }
  .pt-buddy-news { grid-template-columns: 44px minmax(0, 1fr); gap: 12px; padding: 17px 15px; }
  .pt-buddy-news-icon { width: 44px; height: 44px; border-radius: 14px; font-size: 22px; }
  .pt-home-progress, .pt-home-ready, .pt-home-error { grid-template-columns: 1fr; gap: 12px; padding: 18px 16px; }
  .pt-home-progress-buddy { width: 82px; }
  .pt-buddy-destinations { padding: 16px; }
  .pt-buddy-destination-grid { grid-template-columns: 1fr; }
  .pt-buddy-destination { min-height: 0; padding: 16px; }
  .pt-home-buddy-guide { grid-template-columns: 1fr; gap: 10px; padding: 0; }
  .pt-home-buddy-guide-art { width: 76px; padding: 7px 8px 0; border-radius: 18px; }
  .pt-home-buddy-guide-main { gap: 10px; }
  .pt-home-buddy-quick,
  .pt-home-buddy-interactive,
  .pt-home-buddy-workspace { padding: 16px; }
  .pt-home-buddy-topics { grid-column: 1 / -1; }
  .pt-home-buddy-interactive { grid-template-columns: 1fr; }
  .pt-home-buddy-interactive button { width: 100%; }
  .pt-home-buddy-workspace-heading { align-items: stretch; flex-direction: column; }
  .pt-home-buddy-workspace-actions { align-items: stretch; flex-direction: column; }
  .pt-saved-ideas-heading { align-items: flex-start; flex-direction: column; }
  .pt-saved-ideas-save,
  .pt-saved-ideas-load { grid-template-columns: 1fr; }
  .pt-saved-ideas button { width: 100%; }
  .pt-home-ready-actions { justify-content: stretch; }
  .pt-home-ready-actions button { width: 100%; }
  body.pt-creator-open .pt-arrival {
    display: flex !important;
    flex-direction: column;
    width: calc(100% - 24px);
    margin-top: -22px;
    padding: 20px 16px 26px;
    border-radius: var(--pt-radius);
  }
  body.pt-creator-open .pt-creator-header { order: 0; }
  body.pt-creator-open .pt-starting-point { order: 1; }
  body.pt-creator-open .pt-creator-rail { order: 2; width: 100%; gap: 14px; }
  body.pt-creator-open .pt-prompt { order: 3; }
  body.pt-creator-open .pt-arrival-buddy-stage { min-height: 0; padding: 2px 8px 0; }
  .pt-creator-buddy-presence { flex-direction: row; gap: 14px; text-align: left; }
  .pt-creator-buddy-character { width: 92px; max-width: 32%; flex: 0 0 auto; }
  .pt-creator-buddy-presence p { max-width: 250px; margin: 0; }
  body.pt-creator-open .pt-quality { align-self: stretch; }
  body.pt-creator-open .pt-commit { order: 5; }
  body.pt-creator-open .pt-cta-cost { order: 6; }
  body.pt-creator-open #startingModeRow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pt-creator-howto { padding: 14px; }
  .pt-creator-howto ol { grid-template-columns: 1fr; gap: 8px; }
  .pt-creator-howto li { min-height: 0; }
  body.pt-creator-open:not([data-pt-creator-mode="lyrics"]) #detailsCard #details { min-height: 180px; }
  .pt-creator-header { grid-template-columns: 72px minmax(0, 1fr); gap: 12px; }
  .pt-creator-back { grid-column: 1 / -1; justify-self: start; }
  .pt-creator-logo { width: 72px; max-width: 72px; }
}
@media (min-width: 720px) and (max-width: 1050px) {
  .pt-home-trust-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pt-home-trust-card-buddy { grid-column: 1 / -1; }
}
@media (prefers-reduced-motion: reduce) {
  .pt-welcome-card { transition: none; }
  .pt-welcome-card:hover, .pt-welcome-card:focus-visible { transform: none; }
  .pt-buddy-field-toggle,
  #ptHomeBuddyTalkBtn,
  #ptHomeBuddyEscalate,
  #pbVoiceResultTriggerBtn,
  .pt-buddy-field-toggle::after,
  #ptHomeBuddyTalkBtn::after,
  #ptHomeBuddyEscalate::after,
  #pbVoiceResultTriggerBtn::after { animation: none; }
  .pt-creator-buddy-character { animation: none; }
}
.pt-library-explicit-badge {
  display: inline-block;
  margin-right: 0.55rem;
  padding: 0.14rem 0.42rem;
  border: 1px solid currentColor;
  border-radius: 0.3rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}

.pt-library-explicit-badge[hidden] { display: none; }

.pt-first-login-welcome[hidden] { display: none !important; }
.pt-first-login-welcome { z-index: 1200; padding: 20px; }
.pt-first-login-panel {
  width: min(780px, 100%);
  max-height: calc(100vh - 40px);
  display: grid;
  grid-template-columns: minmax(230px, .82fr) minmax(300px, 1.18fr);
  overflow: auto;
  background: var(--pt-surface, #1b2230);
  border: 1px solid var(--pt-line, rgba(255,255,255,.16));
  border-radius: 24px;
  box-shadow: 0 24px 72px rgba(0,0,0,.48);
}
.pt-first-login-media { position: relative; min-height: 470px; background: #111725; overflow: hidden; }
.pt-first-login-media video { display: block; width: 100%; height: 100%; object-fit: cover; }
.pt-first-login-play { position: absolute; left: 20px; bottom: 22px; }
.pt-first-login-copy { align-self: center; padding: 34px; }
.pt-first-login-copy h2 { margin: 6px 0 12px; font-family: var(--pt-font-display, inherit); }
.pt-first-login-copy p, .pt-first-login-copy li { color: var(--pt-ink-soft, rgba(255,255,255,.78)); line-height: 1.5; }
.pt-first-login-copy ul { margin: 22px 0; padding-left: 20px; display: grid; gap: 10px; }
.pt-first-login-copy > button { width: 100%; margin-top: 9px; }
.pt-first-login-status { min-height: 22px; margin: 0 0 4px; }
.pt-first-login-transcript { margin-top: 12px; color: var(--pt-ink-soft, rgba(255,255,255,.78)); }
.pt-first-login-transcript summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; justify-content: center; color: var(--pt-ink, #fff); font-weight: 700; }
.pt-first-login-transcript p { margin: 10px 0 0; text-align: left; line-height: 1.55; }
.pt-marketing-email-setting { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--pt-line, rgba(255,255,255,.14)); }
.pt-marketing-email-setting label { display: flex; gap: 10px; align-items: flex-start; text-transform: none; letter-spacing: normal; line-height: 1.45; cursor: pointer; }
.pt-marketing-email-setting input { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 2px; }
.pt-marketing-email-setting button { width: 100%; margin-top: 10px; }
.pt-marketing-email-setting p { min-height: 18px; margin: 7px 0 0; color: var(--pt-ink-soft, rgba(255,255,255,.75)); font-size: 12px; }

/* Account sign-in providers: a single, calm authentication block instead of
   two unrelated controls pressed against the legal acceptance copy. */
.pt-social-auth {
  margin: 18px 0 16px;
  padding: 16px;
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 16px;
  background: rgba(8,12,21,.34);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.035);
}
.pt-social-auth-heading { margin-bottom: 13px; text-align: center; }
.pt-social-auth-heading h3 {
  margin: 0;
  color: var(--text);
  font-size: 15px;
  letter-spacing: .01em;
  text-transform: none;
}
.pt-social-auth-heading p {
  margin: 5px auto 0;
  max-width: 390px;
  color: var(--text-muted);
  font-size: 12px;
  line-height: 1.45;
}
/* The host for Google's own rendered button. It is given exactly the same box
   as the three PorchTunes-drawn buttons, and GIS is asked for that same width
   in app.js, so the official button lands flush in the stack instead of
   floating at its own intrinsic size. */
/* ===================================================================
   [PT-20260925-029] The host for Google's OWN rendered button.

   THE BUG THIS FIXES. The previous rule did two things that fight each other:
   it forced `width: 100% !important` onto the element Google renders, and it
   set `overflow: hidden` on the box around it. Google Identity Services bakes
   a pixel width into its button at render time and lays the contents out for
   exactly that width. Stretching the element wider than the width GIS was
   asked for pushes its right-hand content past the box, and `overflow:hidden`
   then cuts it off - which on the PERSONALIZED button ("Continue as ...",
   with the avatar and name on the left) is precisely where the multicolour
   Google logo sits. The logo was clipped on its right edge.

   The fix is to stop fighting GIS. Nothing here resizes, scales, stretches or
   crops Google's button: the width is requested through GIS's own `width`
   option (see renderGoogleButton in app.js, which measures this host and
   clamps to GIS's supported 200-400px range), the host simply centres
   whatever GIS draws, and nothing is clipped. That keeps the official button
   and its branding exactly as Google renders it, while its left and right
   edges still line up with the three PorchTunes-drawn buttons below.
   =================================================================== */
.pt-google-signin {
  display: flex;
  align-items: center;
  justify-content: center;   /* centred, never stretched */
  width: 100%;
  min-height: var(--pt-auth-btn-h);
  /* NO overflow:hidden - clipping Google's button is what caused the defect.
     NO border-radius either: the radius belonged to a clipping box that no
     longer exists, and GIS draws its own corners. */
}
/* [PT-20260925-031] Google's element keeps its own intrinsic size, FULL STOP.
   The only thing overridden is a stray margin GIS sometimes emits, which
   would knock it off centre.

   `max-width: 100%` used to sit here and was the second half of the clipping
   defect. GIS renders its wrapper AND the button inside it for one width. A
   max-width squeezes only the wrapper; the button keeps the width GIS drew it
   at and spills out of the right-hand side. Measured live on Production: a
   300px wrapper containing a 320px button, overhanging by 20px. Constraining
   Google's output is exactly what we must not do - the width is requested
   through GIS's own option instead, and nothing here second-guesses it.

   `min-width: 0` on every descendant is gone for the same reason: it is a
   blanket override of Google's internal layout. */
.pt-google-signin > div { margin: 0 !important; }
/* ===================================================================
   [PT-20260925-028] ONE button geometry for all four sign-in choices.
   
   The previous base rule painted every button Facebook Blue and let each
   provider override it, so the shared :hover rule repainted Apple and email
   Facebook-blue on hover, and the Google fallback inherited Facebook's colour
   outright. Geometry and brand are now separated: this block owns size,
   spacing, type and border radius ONLY, and each provider block below owns
   its own colours and its own hover/active states.

   The metrics deliberately match what Google Identity Services renders for
   size:"large" / shape:"rectangular" / logo_alignment:"left" - 44px tall, 4px
   radius, 20px mark hard against the left edge, label optically centred in
   the remaining space - so the official Google button and the three
   PorchTunes-drawn buttons form one aligned stack instead of four
   near-misses.
   =================================================================== */
.pt-social-auth-button {
  position: relative;
  box-sizing: border-box;
  width: 100%;
  height: var(--pt-auth-btn-h);
  min-height: var(--pt-auth-btn-h);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 0;
  padding: 0 14px 0 48px;
  border-radius: var(--pt-auth-btn-radius);
  font: 500 14px/1.2 "Roboto", "Segoe UI", system-ui, -apple-system, sans-serif;
  letter-spacing: .01em;
  text-transform: none;
  box-shadow: none;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
/* The mark sits at the left edge; the label stays optically centred. This is
   what makes four different provider marks line up in one vertical rule. */
.pt-social-auth-button .pt-social-auth-icon {
  position: absolute;
  left: 13px;
  top: 50%;
  transform: translateY(-50%);
}
.pt-social-auth-label {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pt-social-auth-button:focus-visible {
  outline: 3px solid rgba(121,177,255,.52);
  outline-offset: 2px;
}

/* --- Per-provider branding. Each mark, colour and label follows that
       provider's own published guidance; none is redrawn or recoloured. --- */

/* Google: white surface, #747775 stroke, dark-grey label, four-colour G.
   Identical to what GIS itself renders with theme:"outline", so the fallback
   and the real button are visually interchangeable. */
.pt-google-auth-button { background: #fff; color: #1f1f1f; border: 1px solid #747775; }
.pt-google-auth-button:hover:not([disabled]) { background: #f7f8f8; box-shadow: 0 1px 2px rgba(0,0,0,.18); }
.pt-google-auth-button:active:not([disabled]) { background: #eef0f1; }

/* Facebook: Facebook Blue #1877F2 with the supplied white "f" glyph. */
.pt-facebook-auth-button { background: #1877f2; color: #fff; border: 1px solid #1877f2; }
.pt-facebook-auth-button:hover:not([disabled]) { background: #166fe5; border-color: #166fe5; }
.pt-facebook-auth-button:active:not([disabled]) { background: #1464d1; border-color: #1464d1; }

/* Apple: black surface, white label and white Apple logo. */
.pt-apple-auth-button { background: #000; color: #fff; border: 1px solid #000; }
.pt-apple-auth-button:hover:not([disabled]) { background: #1a1a1a; border-color: #1a1a1a; }
.pt-apple-auth-button:active:not([disabled]) { background: #2a2a2a; border-color: #2a2a2a; }

/* Email: PorchTunes' own, deliberately quieter than the three branded marks
   so it reads as the alternative rather than a fourth logo. */
.pt-email-auth-button {
  background: rgba(255,255,255,.07);
  color: var(--text);
  border: 1px solid rgba(255,255,255,.26);
}
.pt-email-auth-button:hover:not([disabled]) {
  background: rgba(255,255,255,.12);
  border-color: rgba(255,255,255,.38);
}
.pt-email-auth-button:active:not([disabled]) { background: rgba(255,255,255,.16); }

.pt-facebook-auth-icon { display: inline-grid; place-items: center; }
.pt-facebook-auth-icon svg { display: block; }

/* A control that cannot act must not look or behave as though it can - no
   hover repaint, no pointer, no tab stop (set in app.js), and a cursor that
   says "not available" rather than "working on it". */
.pt-social-auth-button[disabled] {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
  box-shadow: none;
}
.pt-social-auth-button[aria-busy="true"] { opacity: .7; cursor: progress; }
.pt-social-auth-spinner {
  position: absolute; right: 13px; top: 50%; margin-top: -8px;
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.45); border-top-color: #fff;
  animation: pt-social-spin .8s linear infinite;
}
.pt-social-auth-spinner[hidden] { display: none; }
@keyframes pt-social-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .pt-social-auth-spinner { animation-duration: 2.4s; }
  .pt-social-auth-button { transition: none; }
}

/* [PT-20260924-004] Sign In / Create Account as two unmistakable modes. */
.pt-auth-modes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin: 0 0 10px;
  padding: 4px;
  border-radius: 10px;
  background: rgba(255,255,255,.06);
}
.pt-auth-mode {
  margin: 0;
  min-height: 44px;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  color: var(--text-muted);
  font: 700 13px/1 system-ui, sans-serif;
  letter-spacing: .01em;
  text-transform: none;
  box-shadow: none;
  cursor: pointer;
}
.pt-auth-mode[aria-checked="true"] {
  background: var(--copper, #b87333);
  border-color: rgba(255,255,255,.22);
  color: #1b1208;
}
.pt-auth-mode:focus-visible { outline: 3px solid rgba(121,177,255,.52); outline-offset: 2px; }
#signInView input[type="email"],
#signInView input[type="password"],
#signInView input[type="text"] { max-width: 100%; box-sizing: border-box; font-size: 16px; }
.pt-auth-divider {
  display: grid;
  grid-template-columns: minmax(24px,1fr) auto minmax(24px,1fr);
  align-items: center;
  gap: 11px;
  margin: 14px 0 14px;
  color: var(--text-muted);
}
/* [PT-20260925-028] The divider is a real separator between the provider
   stack and the expanded email form, not decoration. An always-present rule
   with nothing under it is the "broken spacing" this replaces, so it hides
   outright until there is something to separate. */
.pt-auth-divider[hidden] { display: none; }
.pt-auth-divider span { height: 1px; background: rgba(255,255,255,.15); }
.pt-auth-divider strong {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .055em;
  text-transform: uppercase;
}
@media (max-width: 640px) {
  .pt-first-login-welcome { padding: 10px; align-items: flex-start; }
  .pt-first-login-panel { grid-template-columns: 1fr; max-width: 390px; max-height: calc(100vh - 20px); border-radius: 20px; }
  .pt-first-login-media { min-height: 390px; }
  .pt-first-login-copy { padding: 24px; }
  .pt-social-auth { padding: 14px 12px; }
}

/* [PT-20260925-028] Phone layout. One column, thumb-sized targets, and type
   that does not force iOS to zoom the viewport on focus. */
@media (max-width: 600px) {
  :root { --pt-auth-btn-h: 48px; --pt-auth-stack-gap: 12px; }
  .pt-social-auth { margin: 14px 0 12px; padding: 14px 12px; border-radius: 14px; }
  .pt-social-auth-heading h3 { font-size: 16px; }
  .pt-social-auth-heading p { font-size: 12.5px; }
  .pt-social-auth-button { font-size: 15px; padding: 0 12px 0 46px; }
  .pt-social-auth-button .pt-social-auth-icon { left: 14px; }
  .pt-auth-divider { margin: 12px 0; }
}
/* Very narrow phones: the label must shrink before it truncates. */
@media (max-width: 360px) {
  .pt-social-auth-button { font-size: 14px; padding: 0 10px 0 42px; }
  .pt-social-auth-button .pt-social-auth-icon { left: 11px; }
}
@media (prefers-reduced-motion: reduce) {
  .pt-first-login-welcome, .pt-first-login-panel { scroll-behavior: auto; }
}

/* [PT-20260923-001] Library title search. Sits above the rating tabs in both
   the Songs and Videos panes and uses the same surface treatment as the rest
   of the Library chrome. */
.pt-library-search {
  margin: 0 0 8px;
}
.pt-library-search-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.05);
  color: var(--pt-ink);
  font-size: 16px;
  line-height: 1.3;
  /* iOS automatically enlarges pages when a focused form control is below
     16px.  That enlargement can survive a single-page view transition and
     make Create look permanently zoomed after leaving the Library. */
}

/* Keep every editable control above Safari's focus-zoom threshold.  This is
   deliberately limited to touch-sized phone viewports; desktop typography
   and the user's normal pinch-to-zoom accessibility remain unchanged. */
@media (max-width: 600px) and (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
  textarea,
  select { font-size: 16px; }
}
.pt-library-search-input::placeholder { color: var(--pt-ink-soft); opacity: 0.8; }
.pt-library-search-input:focus {
  outline: 2px solid rgba(255, 255, 255, 0.35);
  outline-offset: 1px;
}

/* [PT-20260924-018] Authentication experience: mobile-first, one card, two
   unmistakable choices, generous targets, visible focus. Provider logos are
   never restyled - only the surrounding layout. */
.pt-auth-panel { display: block; }
.pt-auth-heading {
  font-size: 22px; line-height: 1.25; margin: 0 0 6px; letter-spacing: 0;
  text-transform: none;
}
.pt-auth-sub { font-size: 14px; line-height: 1.5; color: var(--text-muted); margin: 0 0 18px; }
.pt-auth-choice {
  display: block; width: 100%; margin: 0 0 12px; padding: 16px 18px;
  min-height: 64px; text-align: left; border-radius: 12px; text-transform: none;
  letter-spacing: 0; box-shadow: none;
}
.pt-auth-choice-title { display: block; font-size: 16px; font-weight: 700; line-height: 1.3; }
.pt-auth-choice-note { display: block; font-size: 12.5px; font-weight: 400; opacity: .82; margin-top: 3px; }
.pt-auth-choice:focus-visible,
.pt-auth-email-toggle:focus-visible,
.pt-auth-mode:focus-visible,
.pt-auth-back:focus-visible,
.pt-show-password:focus-visible,
.pt-auth-link:focus-visible { outline: 3px solid rgba(121,177,255,.55); outline-offset: 2px; }
.pt-auth-back {
  background: none; border: none; color: var(--text-muted); font-size: 13px;
  padding: 6px 0; margin: 0 0 6px; min-height: 36px; cursor: pointer;
  text-transform: none; letter-spacing: 0; box-shadow: none;
}
.pt-auth-email-toggle {
  width: 100%; min-height: 48px; margin: 0; border-radius: 10px;
  text-transform: none; letter-spacing: 0; font-weight: 600; font-size: 15px;
}
.pt-password-row { display: flex; gap: 8px; align-items: stretch; }
.pt-password-row input { flex: 1 1 auto; min-width: 0; }
.pt-show-password {
  flex: 0 0 auto; min-width: 72px; min-height: 44px; margin: 0; border-radius: 8px;
  font-size: 13px; text-transform: none; letter-spacing: 0; box-shadow: none;
}
.pt-consent-row,
.pt-remember-row {
  display: flex; align-items: flex-start; gap: 10px; margin: 0 0 14px;
  font-size: 12.5px; line-height: 1.5; text-transform: none; letter-spacing: normal;
  cursor: pointer;
}
.pt-remember-row { align-items: center; margin: 12px 0 0; }
.pt-consent-row input, .pt-remember-row input { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 1px; }
.pt-auth-link {
  background: none; border: none; color: var(--copper); font-size: 13px;
  padding: 10px 0; min-height: 40px; cursor: pointer; text-decoration: underline;
  text-transform: none; letter-spacing: 0; box-shadow: none;
}
.pt-auth-switch { display: grid; gap: 6px; margin: 18px 0 0; }
.pt-auth-mode {
  margin: 0; min-height: 44px; border: 1px solid rgba(255,255,255,.16);
  border-radius: 9px; background: transparent; color: var(--text-muted);
  font: 600 13px/1.3 system-ui, sans-serif; letter-spacing: 0; text-transform: none;
  box-shadow: none; cursor: pointer; padding: 10px 12px;
}
.pt-auth-mode[aria-checked="true"] { display: none; }
.pt-auth-hint { font-size: 12.5px; color: var(--text-muted); margin: 10px 0 0; line-height: 1.5; }
.pt-auth-error { font-size: 12.5px; color: #ff9d8a; margin: 10px 0 0; line-height: 1.5; }
.pt-auth-help { margin-top: 14px; font-size: 12.5px; }
.pt-auth-help p { font-size: 12.5px; line-height: 1.6; color: var(--text-muted); margin: 8px 0 0; }
.pt-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
#signInView input[type="email"],
#signInView input[type="password"],
#signInView input[type="text"] {
  max-width: 100%; box-sizing: border-box; font-size: 16px; min-height: 46px;
}
@media (min-width: 620px) {
  .pt-auth-heading { font-size: 25px; }
  .pt-auth-switch { grid-template-columns: 1fr; }
}

/* ===================================================================
   [PT-20260925-016] Account & Settings accordion
   One surface, collapsed by default, single-open. Targets are >=48px so
   a thumb on a phone can hit a header without hitting its neighbour.
   =================================================================== */
.pt-settings-accordion { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.pt-set-sec {
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 12px;
  background: rgba(255, 255, 255, .03);
  overflow: hidden;
}
.pt-set-sec[hidden] { display: none; }
.pt-set-head-wrap { margin: 0; font-size: inherit; font-weight: inherit; }
.pt-set-head {
  display: grid;
  grid-template-columns: 1fr auto auto;
  grid-template-areas: "title status chev" "hint hint chev";
  align-items: center;
  gap: 2px 10px;
  width: 100%;
  min-height: 56px;
  padding: 12px 14px;
  background: none;
  border: 0;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.pt-set-head:hover { background: rgba(255, 255, 255, .05); }
/* Never remove the focus ring - keyboard users navigate this with arrows. */
.pt-set-head:focus-visible { outline: 2px solid var(--accent, #e88c30); outline-offset: -2px; }
.pt-set-title  { grid-area: title;  font-size: 15px; font-weight: 600; }
.pt-set-status { grid-area: status; font-size: 12px; color: var(--text-muted); text-align: right; }
.pt-set-hint   { grid-area: hint;   font-size: 12px; color: var(--text-muted); }
.pt-set-chev {
  grid-area: chev; width: 10px; height: 10px; border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor; transform: rotate(45deg); opacity: .6;
  transition: transform .15s ease; margin-left: 4px;
}
.pt-set-sec.is-open .pt-set-chev { transform: rotate(-135deg); }
.pt-set-body { padding: 4px 14px 16px; border-top: 1px solid rgba(255, 255, 255, .08); }
.pt-set-body[hidden] { display: none; }
.pt-set-sub { font-size: 13px; margin: 14px 0 6px; }
.pt-set-note { font-size: 12px; color: var(--text-muted); margin: 6px 0 0; }
.pt-set-kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; margin: 8px 0 0; font-size: 13px; }
.pt-set-kv dt { color: var(--text-muted); }
.pt-set-kv dd { margin: 0; word-break: break-word; }
.pt-set-subtoggle {
  display: block; width: 100%; min-height: 44px; margin-top: 12px;
  background: none; border: 1px dashed rgba(255, 255, 255, .25);
  border-radius: 10px; color: var(--text); font-size: 13px; cursor: pointer;
}
.pt-set-subtoggle:focus-visible { outline: 2px solid var(--accent, #e88c30); outline-offset: 2px; }
.pt-set-linked { margin-top: 10px; padding: 10px 12px; border-radius: 10px; background: rgba(90, 200, 120, .12); }
.pt-set-danger { display: inline-block; margin-top: 12px; font-size: 13px; color: #ff9d8a; }
.pt-signout-row { margin-top: 14px; }

/* Connected sign-in methods -------------------------------------------- */
.pt-methods { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.pt-method {
  display: grid; grid-template-columns: 22px 1fr auto auto; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: 10px; background: rgba(255, 255, 255, .04); font-size: 13px;
}
.pt-method-icon { width: 20px; height: 20px; }
.pt-method-state { font-size: 12px; color: var(--text-muted); }
.pt-method-state.is-connected  { color: #7ddca4; }
.pt-method-state.is-unavailable { opacity: .65; }
.pt-method-action {
  min-height: 40px; padding: 0 12px; border-radius: 8px; font-size: 12px; cursor: pointer;
  background: none; border: 1px solid rgba(255, 255, 255, .25); color: var(--text);
}
.pt-method-action[hidden] { display: none; }

/* Four-provider authentication stack ------------------------------------ */
/* One set of tokens drives every button, the GIS host and the divider, so a
   size or radius change cannot land on three of the four. */
:root {
  --pt-auth-btn-h: 44px;
  --pt-auth-btn-radius: 4px;
  --pt-auth-stack-gap: 10px;
}
.pt-social-auth-options {
  display: flex;
  flex-direction: column;
  gap: var(--pt-auth-stack-gap);
}
.pt-provider-row { display: flex; flex-direction: column; gap: 4px; }
.pt-provider-note {
  margin: 0;
  padding-left: 2px;
  font-size: 12px;
  line-height: 1.35;
  color: var(--text-muted);
}
.pt-provider-note[hidden] { display: none; }
/* A provider without real configuration is shown, but plainly inert. The
   button's own [disabled] rule above already removes hover and pointer
   events; this only dims the row's note with it. */
.pt-provider-row.is-unavailable .pt-provider-note { opacity: .85; }
.pt-social-auth-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
}
.pt-social-auth-icon svg { display: block; width: 20px; height: 20px; }

@media (max-width: 600px) {
  .pt-set-head { min-height: 60px; }
  .pt-method { grid-template-columns: 22px 1fr; row-gap: 6px; }
  .pt-method-state { grid-column: 2; }
  .pt-method-action { grid-column: 2; justify-self: start; }
}

/* ===================================================================
   [PT-20260925-021] Two screens: Account, and Account & Settings.
   =================================================================== */
#settingsModal { display: none; }
#settingsModal.open { display: flex; }
#settingsModal[hidden] { display: none; }
html.pt-settings-screen-open,body.pt-settings-screen-open {
  overflow: hidden !important;
  overscroll-behavior: none !important;
}
#settingsModal {
  padding: 16px 12px !important;
  overflow: hidden !important;
  overscroll-behavior: contain;
}
#settingsModal > .modal-panel {
  width: min(100%, 560px) !important;
  max-height: calc(100dvh - 32px) !important;
  overflow-y: auto !important;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  scrollbar-gutter: stable;
}

.pt-settings-header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 10px;
}
.pt-settings-header h2 { margin: 0; font-size: 18px; text-align: center; }
/* The title takes focus on entry so the screen change is announced; it must
   not then render a stray outline for mouse users. */
.pt-settings-header h2:focus { outline: none; }
.pt-settings-header h2:focus-visible { outline: 2px solid var(--accent, #e88c30); outline-offset: 4px; }
.pt-back-btn {
  min-height: 44px;
  padding: 0 12px;
  background: none;
  border: 1px solid rgba(255, 255, 255, .25);
  border-radius: 8px;
  color: var(--text);
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
.pt-back-btn:focus-visible { outline: 2px solid var(--accent, #e88c30); outline-offset: 2px; }

/* Signed-in Account landing: summary only, then the two things you can do. */
.pt-account-landing { display: flex; flex-direction: column; gap: 10px; }
.pt-landing-email { margin: 0; font-size: 13px; color: var(--text-muted); word-break: break-word; }
.pt-landing-kv {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 12px;
  margin: 4px 0 6px;
  font-size: 13px;
}
.pt-landing-kv dt { color: var(--text-muted); }
.pt-landing-kv dd { margin: 0; }
#openSettingsBtn, .pt-account-landing #signOutBtn { width: 100%; min-height: 48px; }

@media (max-width: 600px) {
  .pt-settings-header h2 { font-size: 16px; }
  .pt-back-btn { min-height: 48px; padding: 0 10px; }
}

/* [PT-20260925-022] The updated-policy screen names which policies changed. */
.pt-consent-policies {
  margin: 0 0 12px;
  padding-left: 20px;
  font-size: 13px;
  color: var(--text);
}
.pt-consent-policies[hidden] { display: none; }
.pt-consent-policies li { margin: 2px 0; }

/* [PT-20260925-023] Visually hidden labels: the contact fields already carry
   visible placeholders and an approved layout, so the accessible name is added
   without changing anything on screen. */
.pt-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* [PT-20260925-025] Account summary: display name, and an honest note when
   optional contact details are still missing. Layout is otherwise unchanged. */
.pt-landing-name { margin: 0; font-size: 15px; font-weight: 600; }
.pt-landing-name[hidden] { display: none; }
.pt-landing-incomplete {
  margin: 2px 0 0; font-size: 12px; color: var(--text-muted);
}
.pt-landing-incomplete[hidden] { display: none; }

/* [PT-20260925-026] The `hidden` attribute must actually hide these.
   .pt-social-auth-button sets display:flex, which outranks the UA default of
   [hidden]{display:none} — so with Google live, BOTH the Google Identity
   Services button and the PorchTunes fallback rendered, giving two
   "Continue with Google" rows. Same trap for the GIS container itself. */
.pt-social-auth-button[hidden],
.pt-google-signin[hidden],
.pt-provider-row[hidden] { display: none !important; }

/* ===================================================================
   [PT-20260925-028] Profile & Contact Details.
   Visible labels rather than placeholder-only fields (a placeholder
   disappears the moment someone types, which leaves a filled form with no
   way to tell which value is which), and an explicit "Not provided yet"
   for anything never supplied, so an empty field can never be mistaken
   for a failed load.
   =================================================================== */
.pt-profile-known {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 14px;
  margin: 10px 0 14px;
  padding: 10px 12px;
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: 10px;
  background: rgba(255, 255, 255, .04);
  font-size: 13px;
}
.pt-profile-known dt { color: var(--text-muted); }
.pt-profile-known dd { margin: 0; color: #fff; word-break: break-word; }
.pt-profile-fields-title {
  font-size: 10px; text-transform: uppercase; letter-spacing: .07em;
  color: #fff; opacity: .75; margin: 12px 0 6px;
}
.pt-profile-intro {
  margin: 0 0 10px; font-size: 12px; line-height: 1.45; color: var(--text-muted);
}
.pt-profile-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
.pt-profile-field label {
  font-size: 12px; color: var(--text-muted);
  text-transform: none; letter-spacing: normal;
}
.pt-profile-field input {
  box-sizing: border-box;
  width: 100%;
  min-height: 44px;
  padding: 9px 11px;
  border: 1px solid rgba(255, 255, 255, .25);
  border-radius: 8px;
  background: rgba(255, 255, 255, .15);
  color: #fff;
  font-size: 16px; /* 16px keeps iOS from zooming the viewport on focus. */
}
.pt-profile-field input:focus-visible {
  outline: 2px solid var(--accent, #e88c30); outline-offset: 1px;
}
.pt-profile-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 480px) {
  .pt-profile-row { grid-template-columns: 1fr; gap: 0; }
  .pt-profile-known { grid-template-columns: 1fr; gap: 2px 0; }
  .pt-profile-known dt { margin-top: 6px; }
}

/* [PT-20260925-028] The connection indicator, freed from the duplicate
   "Account" heading it used to hang off. It needs a real line box of its own:
   a first attempt collapsed it with line-height:0, which made the indicator
   overlap the "Signed in as ..." line underneath. It reserves one short row
   instead, and disappears entirely while the indicator carries no state
   It reserves one short row instead. The row is deliberately always present
   rather than conditionally hidden: the indicator renders through ::after on
   a state class, so "has no state" is not something a selector can ask about
   reliably, and a reserved 16px row cannot shift the content under it. */
.pt-account-connection {
  margin: 0 0 6px;
  font-size: 12px;
  line-height: 1.4;
  min-height: 16px;
}
.pt-account-connection .status-indicator { margin-left: 0; }

/* [PT-20260926-001] The one line that explains a free song's limits, so a
   disabled Download button is never left to be guessed at. */
.pt-library-locked-note {
  margin: 8px 0 4px;
  padding: 8px 10px;
  border-radius: 6px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid rgba(255, 255, 255, .12);
  font-size: .9rem;
  line-height: 1.4;
}

/* [PT-20260926-001] Billing & Payment: the customer's current state first,
   then only the actions genuinely available from it. */
.pt-billing-current {
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 12px;
  background: rgba(255, 255, 255, .07);
}
.pt-billing-current .pt-billing-plan-name { font-weight: 600; font-size: 14px; }
.pt-billing-current dl { display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; margin: 8px 0 0; font-size: 12px; }
.pt-billing-current dt { opacity: .7; }
.pt-billing-current dd { margin: 0; }
.pt-billing-section-label {
  font-size: 10px; text-transform: uppercase; letter-spacing: .07em;
  color: #fff; opacity: .75; margin: 14px 0 8px;
}
.pt-billing-changes button, .pt-billing-packs button { width: 100%; margin-bottom: 6px; text-align: left; }
.pt-billing-effect { display: block; font-size: 11px; opacity: .75; margin-top: 2px; }
