/* Runo — the application shell: topbar, navigation, page frame, cards.
 *
 * The nav is built to hold what is coming (usage, billing, admin — KAN-32,
 * KAN-35, KAN-39) rather than only what exists. The old topbar was a brand,
 * a badge and a username, with nowhere to put a fourth thing, which is how
 * every new feature ended up bolted onto the two existing pages.
 */

@layer layout {
  /* --- topbar ---------------------------------------------------------- */
  /* Everything in here must survive being squeezed. A phone zoomed out
     crosses into the desktop layout at a physically tiny size, and the
     first version overflowed: the username escaped from under the nav and
     the brand was covered. Hence min-width:0 throughout, a nav that can
     shed its labels, and no fixed-width children. */
  .topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    height: var(--topbar-h);
    padding-inline: var(--space-4);
    background: color-mix(in oklab, var(--surface) 88%, transparent);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border);
    overflow: hidden;
  }
  .topbar > * { min-width: 0; }

  .brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-serif);
    font-size: var(--text-lg);
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--text);
    text-decoration: none;
    white-space: nowrap;
  }
  .brand:hover { color: var(--text); }
  .brand:hover .brand-mark { transform: rotate(-8deg); }

  /* The mark is an inline SVG so it inherits currentColor and needs no
   * network request. Decorative: the wordmark beside it carries the name. */
  .brand-mark {
    flex: none;
    width: 1.35rem;
    height: 1.35rem;
    color: var(--accent);
    transition: transform var(--duration-base) var(--ease);
  }

  .topbar-nav {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-inline-start: var(--space-2);
    min-width: 0;
  }

  /* Between the bottom-bar breakpoint and a comfortable desktop there is a
     band where the labels do not fit. Hide them rather than let the bar
     overflow — but *visually*, not with display:none, which would take the
     link's only accessible name out of the tree with it. Bounded below by
     the bottom-bar breakpoint, where labels are shown again. */
  @media (min-width: 48.0625rem) and (max-width: 60rem) {
    .topbar-nav .nav-link span {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
    .topbar-nav .nav-link { padding-inline: var(--space-2); }
  }

  .nav-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    height: 2.25rem;
    padding-inline: var(--space-3);
    border-radius: var(--radius-md);
    color: var(--text-muted);
    font-size: var(--text-sm);
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--duration-fast) var(--ease),
                color var(--duration-fast) var(--ease);
  }
  .nav-link:hover {
    background: var(--surface-raised);
    color: var(--text);
  }
  /* aria-current is the accessible truth; the styling follows it rather
   * than a separate .active class that could disagree with it. */
  .nav-link[aria-current="page"] {
    background: var(--accent-subtle);
    color: var(--text);
  }

  .topbar-spacer { flex: 1 1 auto; }

  .topbar-end {
    display: flex;
    align-items: center;
    gap: var(--space-2);
  }

  /* --- page frame ------------------------------------------------------ */
  .page {
    max-width: var(--content-max);
    margin-inline: auto;
    padding: var(--space-5) var(--space-4) var(--space-7);
  }

  .page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
  }

  .page-title {
    font-size: var(--text-2xl);
    letter-spacing: -0.02em;
  }

  .page-subtitle {
    margin-top: var(--space-1);
    color: var(--text-muted);
    font-size: var(--text-sm);
  }

  /* --- card ------------------------------------------------------------ */
  .card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    margin-bottom: var(--space-4);
  }
  .card:last-child { margin-bottom: 0; }

  .card-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
  }

  .card-title {
    font-family: var(--font-serif);
    font-size: var(--text-lg);
  }

  /* Flush card: used where the body is a full-bleed table. */
  .card-flush { padding: 0; overflow: hidden; }
  .card-flush > .card-header {
    padding: var(--space-4) var(--space-5) 0;
    margin-bottom: var(--space-3);
  }

  /* --- stack: the vertical rhythm primitive ---------------------------- */
  .stack > * + * { margin-top: var(--space-4); }
  .stack-sm > * + * { margin-top: var(--space-2); }
  .stack-lg > * + * { margin-top: var(--space-5); }

  /* --- prose ----------------------------------------------------------- */
  .prose {
    max-width: var(--prose-max);
    line-height: var(--leading-prose);
  }

  /* --- empty state -----------------------------------------------------
   * A new account lands on an empty library, and activation is most of the
   * funnel (KAN-31). "Nothing here yet" is not a design.
   */
  .empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-7) var(--space-4);
    text-align: center;
  }
  .empty-icon {
    width: 2.5rem;
    height: 2.5rem;
    color: var(--text-faint);
  }
  .empty-title {
    font-family: var(--font-serif);
    font-size: var(--text-lg);
    color: var(--text);
  }
  .empty-body {
    max-width: 32rem;
    color: var(--text-muted);
    font-size: var(--text-sm);
  }

  /* --- mobile ---------------------------------------------------------- */
  @media (max-width: 48rem) {
    /* **The blur and the clip have to come off here**, and it is not
     * cosmetic. `backdrop-filter` makes an element a containing block for
     * `position: fixed` descendants, and `overflow: hidden` then clips
     * them — so the bottom nav below was fixed to the bottom edge of *the
     * topbar* and rendered at the top of the screen, which is the one place
     * it was written to avoid. It had never worked (found 2026-08-11). The
     * nav carries its own blur instead. */
    .topbar {
      padding-inline: var(--space-3);
      gap: var(--space-2);
      backdrop-filter: none;
      overflow: visible;
      background: var(--surface);
    }
    /* The nav moves to a bottom bar rather than a hamburger: three or four
     * destinations reachable with a thumb beats a menu that hides them. */
    .topbar-nav {
      position: fixed;
      inset: auto 0 0 0;
      z-index: 25;
      margin: 0;
      justify-content: space-around;
      gap: 0;
      height: calc(var(--mobile-nav-h) + env(safe-area-inset-bottom));
      padding-bottom: env(safe-area-inset-bottom);
      background: color-mix(in oklab, var(--surface) 94%, transparent);
      backdrop-filter: blur(12px);
      border-top: 1px solid var(--border);
    }
    .nav-link {
      flex-direction: column;
      gap: 2px;
      height: 100%;
      flex: 1;
      justify-content: center;
      border-radius: 0;
      font-size: var(--text-xs);
    }
    .nav-link[aria-current="page"] {
      background: none;
      color: var(--accent);
      box-shadow: inset 0 2px 0 var(--accent);
    }
    .page {
      padding: var(--space-4) var(--space-3) var(--space-7);
    }
    /* Clear the fixed bottom nav, and the mini player when it is up. Both
     * heights are tokens, so the page cannot drift out of step with the
     * furniture it is making room for. */
    .page {
      padding-bottom: calc(var(--mobile-nav-h) + env(safe-area-inset-bottom)
                           + var(--space-5));
    }
    body.has-player .page {
      padding-bottom: calc(var(--mobile-nav-h) + var(--mini-player-h)
                           + env(safe-area-inset-bottom) + var(--space-5));
    }
    .page-header { flex-direction: column; gap: var(--space-3); }
    .card { padding: var(--space-4); border-radius: var(--radius-md); }
    /* A title and its aside side by side squeeze both into two ragged
       columns at phone width. Stack them instead. */
    .card-header {
      flex-direction: column;
      align-items: flex-start;
      gap: var(--space-1);
    }
  }

  /* Attribution for the shipped voice. Quiet by design — it is a licence
     obligation, not a message — but it sits in the document rather than in
     an "about" nobody opens, because CC BY asks for it where the work is
     served. Padded past the persistent player so it is reachable. */
  .app-footer {
    max-width: var(--content-max);
    margin: var(--space-8) auto 0;
    padding: var(--space-5) var(--space-5) calc(var(--player-h, 5rem) + var(--space-6));
    border-top: 1px solid var(--border);
  }
  /* Centred, and narrower than the content column: a licence line set
     across a full-width page reads as a stray sentence rather than a
     credit, which is what it looked like left-aligned under a wide table. */
  .voice-credit {
    max-width: 46rem;
    margin-inline: auto;
    font-size: var(--text-xs);
    line-height: 1.6;
    text-align: center;
    text-wrap: balance;
  }

  /* The synthetic-audio disclosure (EU AI Act Article 50). Set like the
     licence credit and a step above it in weight: this one is a statement
     about the product a listener is entitled to notice, not a formality.
     Deliberately not a warning colour — it is a fact about what Runo is,
     and styling it as an alert would read as an apology for it. */
  .ai-notice {
    max-width: 46rem;
    margin-inline: auto;
    margin-bottom: var(--space-2);
    font-size: var(--text-xs);
    line-height: 1.6;
    text-align: center;
    text-wrap: balance;
  }
  /* The colour is inherited from .hint; only the claim itself is lifted. */
  .ai-notice strong { color: var(--text); }

  /* Outstanding terms. Above the content and inside the page column, so it
     reads as part of the app rather than as a system alert — nothing is
     wrong, there is something to read. */
  .policy-banner {
    max-width: var(--content-max);
    margin: var(--space-4) auto 0;
    padding: var(--space-3) var(--space-4);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--surface-raised);
  }
  .policy-banner p { margin: 0; font-size: var(--text-sm); }

  /* --- one-handed targets ------------------------------------------------
   * WCAG 2.2 asks 24px; this app asks `--tap-min` (44px) of anything a
   * thumb aims at, because most listening happens on a phone. Measured at
   * 360/390/430px: these were the surfaces still under it. Links sitting
   * inside a sentence are exempt and left alone — padding them would break
   * the line they belong to.
   */
  @media (max-width: 48rem) {
    /* The wordmark is the way home, not decoration. */
    .brand { min-height: var(--tap-min); }
    /* The footer's Terms and Privacy are navigation wearing a hint's
       clothes, so they get a real target. The licence links in the
       sentence beneath them are inline text and stay that way — WCAG's
       target-size rule exempts them, and padding them would break the line
       they belong to. */
    .footer-links {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: var(--space-2);
    }
    .footer-links a {
      display: inline-flex;
      align-items: center;
      min-height: var(--tap-min);
      padding-inline: var(--space-3);
    }
  }
}
