/*
 * The shell's chrome — the entry screens and the workspace frame.
 *
 * Served at `/shell.css` and linked from `index.html` *after* `/hearth.css`,
 * rather than built at runtime with `document.createElement("style")` as it was
 * until H5 (#221): an inline stylesheet is what forced `style-src
 * 'unsafe-inline'` into the response policy, and that directive was the only
 * hole in an otherwise closed CSP. See `docs/web-shell.md` § "Response headers".
 *
 * It is a file under `public/` rather than a build output because it needs no
 * build: nothing here is imported, generated or transformed, so a bundler step
 * whose only product is a copy would add a way for `/shell.css` to be stale.
 *
 * Since #295 this sheet spends no colour, type or spacing of its own. Every
 * value it uses is a `--hearth-*` token from the foundation and every control
 * shape is a `hearth-ui-*` recipe, which is what `docs/design-system.md` means
 * by adoption: what is left here is the shell's *layout* — where the rail sits,
 * how wide the entry card is, and which of the two forms the chrome takes at a
 * given width. `apps/web/src/shell-chrome.test.ts` holds that line, including
 * the rule that nothing here reaches for a semantic colour on the rail.
 */

/*
 * Wrapped in `hearth.shell`, the layer `hearth.css` declares for the shell
 * (#294). The wrap is what stops this sheet outranking every shared recipe,
 * since an unlayered rule beats a layered one whatever its specificity — and it
 * is also what lets the rules below override a recipe *deliberately*, by rank
 * rather than by out-specifying it.
 */
@layer hearth.shell {
  /*
   * `:root { color-scheme: light dark; --hearth-gap: 1rem }` was here and has
   * moved to `hearth.css`, which is the one value change the wrap forced. A
   * copy surviving in this layer would not have been inert: `hearth.shell`
   * outranks `hearth.base` whatever the specificity, so it would have beaten
   * the foundation's `:root[data-hearth-theme="dark"] { color-scheme: dark }`
   * and left the explicit theme override unable to switch the native widgets.
   *
   * The `body` rule that stood here has gone the same way for a different
   * reason. It set `font: 15px/1.5 …`, and from `hearth.shell` that beat the
   * foundation's `body { font-size: var(--hearth-font-size-md) }` on every
   * screen in Hearth — a whole product one rank below a size the type scale
   * does not contain. Deleting it is the adoption; `hearth.reset` already
   * zeroes the margin.
   */

  /* ---------------------------------------------------------------------
   * The frame
   * ------------------------------------------------------------------ */

  /*
   * One column: the chrome, the two live regions, then the app.
   *
   * This is also the entry screen's frame and the frame of a notice that has
   * replaced the whole root, both of which are a single child in the same box.
   */
  #shell {
    display: flex;
    flex-direction: column;
    height: 100vh;
  }

  /*
   * From `64rem` the shell may reserve a persistent `15rem` app rail
   * (`docs/design-system.md` § "Responsive behaviour"), and this is where it
   * does. A grid rather than a flex row: the rail stands beside *three*
   * stacked children, and the alternative is a wrapper element that exists
   * only to be flexed.
   *
   * Every child is placed explicitly, which is the part that is not cosmetic.
   * Both live regions collapse out of flow while empty (`:empty`, below), and
   * that is the state the workspace opens in — so under auto-placement the app
   * region would slide up into an `auto` track and stop filling the height
   * whenever the shell had nothing to say. Explicit rows do not move.
   *
   * DOM order is unchanged and nothing is reordered: the chrome is still first
   * in the document and first in the reading and focus order, and the rail is
   * where a left-to-right reader meets it first.
   */
  @media (min-width: 64rem) {
    #shell {
      display: grid;
      grid-template-columns: 15rem minmax(0, 1fr);
      grid-template-rows: auto auto minmax(0, 1fr);
    }

    .hearth-header {
      grid-column: 1;
      grid-row: 1 / -1;
    }

    .hearth-realtime-status {
      grid-column: 2;
      grid-row: 1;
    }

    .hearth-notice {
      grid-column: 2;
      grid-row: 2;
    }

    .hearth-region {
      grid-column: 2;
      grid-row: 3;
    }

    /*
     * After the two rules above, and the order is load-bearing rather than
     * tidy. A whole-screen notice carries `.hearth-notice` *and*
     * `.hearth-screen`; both are one class, so specificity cannot separate them
     * and source order is the whole of the cascade between them. Left in
     * column 2 the loading and fatal-error screens would sit beside a rail that
     * is not on the page, in the shadow of a 15rem gutter.
     */
    .hearth-card,
    .hearth-screen {
      grid-column: 1 / -1;
      grid-row: 1;
    }
  }

  /* ---------------------------------------------------------------------
   * The app rail, and its compact form
   * ------------------------------------------------------------------ */

  /*
   * The chrome is `.hearth-ui-rail`, so the coal ground, the navigation
   * palette and the `nav-focus` ring all come from the recipe. What is added
   * here is the shell's own layout: a rail is as tall as the workspace, and its
   * app list grows while the account block stays at the bottom.
   *
   * The recipe's default *is* the rail, so the wide layout restates nothing.
   * The compact bar is the media query below.
   */
  .hearth-header {
    gap: var(--hearth-space-2);
    overflow-y: auto;
  }

  .hearth-brand {
    padding: var(--hearth-space-2) var(--hearth-space-3);
    font-size: var(--hearth-font-size-lg);
    line-height: var(--hearth-line-height-tight);
  }

  .hearth-switcher {
    display: flex;
    flex-direction: column;
    gap: var(--hearth-space-1);
  }

  /*
   * Pushed to the far end of the rail, and separated by a line rather than by
   * distance alone — `nav-border`, which is the separator token for exactly
   * this. `margin-block-start: auto` is what makes "the bottom" mean the bottom
   * of the viewport on a short app list and the bottom of the content on a long
   * one.
   */
  .hearth-account {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--hearth-space-2);
    margin-block-start: auto;
    padding-block-start: var(--hearth-space-3);
    border-block-start: 1px solid var(--hearth-color-nav-border);
    color: var(--hearth-color-nav-text);
    font-size: var(--hearth-font-size-sm);
  }

  /*
   * Who you are, as a caption rather than a control. `nav-text-muted` is the
   * navigation table's own use for it — "unselected labels, captions, account
   * role" — and it is what keeps the name from competing with the app list for
   * the eye while staying comfortably readable at 9.04:1.
   */
  .hearth-account-name {
    padding-inline: var(--hearth-space-1);
    color: var(--hearth-color-nav-text-muted);
    overflow-wrap: anywhere;
  }

  .hearth-invite {
    display: flex;
    flex-direction: column;
    gap: var(--hearth-space-2);
  }

  /*
   * Every control drawn on the rail, in one place.
   *
   * The recipe fills a button with `surface` — near-white — which on coal is a
   * row of paper tiles rather than navigation. These take the navigation
   * vocabulary the document reserves for this ground: a transparent fill over
   * the rail, a `nav-border-control` boundary (4.59:1 on `nav-surface`, where
   * `border-control` is not measured at all), and `nav-text` labels. The focus
   * ring is already `nav-focus` — the foundation scopes that to
   * `.hearth-ui-rail`, because light-mode `focus` is 2.45:1 here, under WCAG
   * 1.4.11's 3:1.
   */
  .hearth-header .hearth-ui-button {
    border-color: var(--hearth-color-nav-border-control);
    background: transparent;
    color: var(--hearth-color-nav-text);
  }

  .hearth-header .hearth-ui-button:hover {
    background: var(--hearth-color-nav-surface-muted);
    color: var(--hearth-color-nav-text);
  }

  /*
   * The minted link. `hearth-ui-input[readonly]` fills with `surface-muted`,
   * which is a near-white panel dropped onto the rail; on this ground the
   * measured pair is `nav-text` on `nav-surface-muted` (13.10:1). It wins by
   * layer rank rather than by specificity — one class and an attribute would
   * otherwise beat one class and an element.
   *
   * The width is the rail's, not the input's: `22rem` would overflow a `15rem`
   * column and put a horizontal scrollbar under the whole workspace.
   */
  .hearth-invite input {
    width: 100%;
    min-width: 0;
    background: var(--hearth-color-nav-surface-muted);
    border-color: var(--hearth-color-nav-border-control);
    color: var(--hearth-color-nav-text);
    font-size: var(--hearth-font-size-sm);
  }

  /*
   * Below the rail width the chrome is a compact horizontal bar.
   *
   * The shell owns this change, and it is CSS rather than a `--bar` class on
   * the element because it is a fact about the viewport: a class would mean the
   * shell listening for resizes to keep a style honest, and `docs/design-
   * system.md` puts the rail-to-bar change here. `--bar` stays available to a
   * surface that renders a bar unconditionally.
   *
   * The edge moves with the form. Left alone, a horizontal bar would carry the
   * recipe's vertical hairline at its inline end and draw no line at all
   * between itself and the workspace beneath it.
   */
  @media (max-width: 63.999rem) {
    .hearth-header {
      flex-direction: row;
      align-items: center;
      gap: var(--hearth-space-3);
      padding: var(--hearth-space-2) var(--hearth-space-3);
      border-inline-end: 0;
      border-block-end: 1px solid var(--hearth-color-nav-border);
      overflow-y: visible;
    }

    .hearth-brand {
      padding: 0;
    }

    /*
     * The switcher scrolls, not the bar: brand, account and sign-out have to
     * stay put, and a member with eight apps must still be able to sign out.
     * Wrapping instead would grow the bar downward and spend the height the app
     * region needs on the layout that has least of it.
     */
    .hearth-switcher {
      flex-direction: row;
      flex-wrap: nowrap;
      overflow-x: auto;
      overscroll-behavior-inline: contain;
    }

    /*
     * A flex item's default is to shrink. In a horizontal scroller that means
     * the tabs compress to fit and nothing ever scrolls — a container that is
     * honest about overflowing and contents that quietly do not.
     */
    .hearth-switcher > * {
      flex: 0 0 auto;
    }

    .hearth-account {
      flex-direction: row;
      align-items: center;
      margin-block-start: 0;
      margin-inline-start: auto;
      padding-block-start: 0;
      border-block-start: 0;
    }

    .hearth-invite {
      flex-direction: row;
      align-items: center;
    }

    .hearth-invite input {
      width: 14rem;
      max-width: 40vw;
    }
  }

  /* ---------------------------------------------------------------------
   * The app region and the chrome's two live regions
   * ------------------------------------------------------------------ */

  .hearth-region {
    flex: 1 1 auto;
    min-height: 0;
    padding: var(--hearth-space-4);
  }

  .hearth-notice {
    padding: var(--hearth-space-2) var(--hearth-space-4);
    color: var(--hearth-color-text-muted);
  }

  .hearth-realtime-status {
    padding: var(--hearth-space-1) var(--hearth-space-4);
    border-block-end: 1px solid var(--hearth-color-border);
    color: var(--hearth-color-text-muted);
    font-size: var(--hearth-font-size-sm);
  }

  /* Collapsed out of flow, not out of the accessibility tree: an empty
     notice must still be exposed, or the message written into it later is
     heard as the region appearing rather than as a change (see notice()).
     Out of flow rather than zero-height because the notice keeps its own
     padding, and in a card it would still be charged the flex gap.
     The whole visually-hidden idiom rather than position alone: overflow and
     clip-path are inert while the selector is :empty, and are what stop this
     from becoming a visible 1px box if it is ever keyed on something else. */
  .hearth-notice:empty,
  .hearth-realtime-status:empty {
    position: absolute; width: 1px; height: 1px;
    padding: 0; overflow: hidden; clip-path: inset(50%);
  }

  /* ---------------------------------------------------------------------
   * Entry, and the screens that replace everything
   * ------------------------------------------------------------------ */

  /*
   * The entry card. `hearth-ui-card` owns the panel — border, radius, surface
   * and the one elevation step a raised card gets — so what is left here is
   * where it sits and how wide it is allowed to be.
   *
   * `26rem` rather than the `22rem` this was before adoption: the recipe's
   * padding is `--hearth-space-5`, and the narrower box left the registration
   * form's three fields and its password sentence noticeably tighter than the
   * same card in the desktop connector.
   */
  .hearth-card {
    display: flex;
    flex-direction: column;
    gap: var(--hearth-space-4);
    max-width: 26rem;
    margin: 12vh auto;
  }

  .hearth-card h1 {
    font-size: var(--hearth-font-size-2xl);
    font-weight: var(--hearth-font-weight-bold);
    line-height: var(--hearth-line-height-tight);
  }

  .hearth-card form {
    display: flex;
    flex-direction: column;
    gap: var(--hearth-space-4);
  }

  /*
   * A notice that *is* the screen — the first paint's "Loading Hearth…", and
   * the dead ends where the session or the app list could not be read. The
   * recipe is `hearth-ui-empty`, which is the document's first-load and
   * retryable-failure treatment; this adds the height that lets it centre in
   * the viewport rather than sitting under the top edge.
   *
   * It keeps `.hearth-notice`, and therefore keeps the `:empty` collapse above
   * — which is what `showNotice` relies on when it inserts the element empty
   * and fills it a statement later.
   */
  .hearth-screen {
    justify-content: center;
    min-height: 60vh;
  }

  /* Both, because one of the shell's refusals is not :disabled. The invite
     button refuses semantically once a link is shown (#156, on #118's
     reasoning), so it would otherwise read as fully live. The recipe says the
     same for `.hearth-ui-button`; this keeps it true of a button that has not
     taken the recipe. */
  button:disabled,
  button[aria-disabled="true"] { cursor: default; opacity: .6; }
}
