/* The application shell (spec §5): the rail, the top bar, the phone tab bar, the content
   container, the toast stack, the auth card, and the three widths. */
@layer shell {
  body.app-shell { display: grid; grid-template-columns: var(--rail-width) minmax(0, 1fr); min-height: 100vh; }
  /* The calculator's Stimulus host (layouts/_calculator.html.erb) is a child of body.app-shell's grid,
     and a box there would take a grid cell of its own; its only content is a <dialog>, which is
     display:none closed and in the top layer open, so the host draws no box at all. */
  .calculator-host { display: contents; }
  /* The collapse cookie sets the rail's TRACK and nothing else; the icon rail below follows the width. */
  body.app-shell.rail--collapsed { grid-template-columns: var(--rail-collapsed-width) minmax(0, 1fr); }

  /* THE RAIL IS A SIZE CONTAINER, `rail`, and the icon-rail block below asks its width. That width
     must stay the grid TRACK's, a length: inline-size containment means the rail's content no longer
     sizes it, so a content-sized track (`auto`, `min-content`) would collapse it to nothing. It also
     makes the rail the containing block of any `position: fixed` descendant (it holds none). */
  .rail {
    position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; gap: var(--space-4);
    padding: 14px 8px; background: var(--bg); border-right: 1px solid var(--line); overflow: hidden auto;
    container: rail / inline-size;
  }
  .brand { display: flex; align-items: center; gap: 10px; padding: 4px 8px; color: var(--fg); font-size: 15px; font-weight: 600; letter-spacing: -0.01em; white-space: nowrap; }
  .brand:hover { text-decoration: none; }
  /* The mark: an orange square with a paper square cut from its centre. */
  .brand-mark { position: relative; width: 18px; height: 18px; flex: 0 0 auto; border-radius: var(--radius-control); background: var(--accent); }
  .brand-mark::after { content: ""; position: absolute; left: 5px; top: 5px; width: 8px; height: 8px; border-radius: 1px; background: var(--bg); }

  /* --- The visitor's header (stage 4, S4-5). The anonymous branch's only chrome: the wordmark and,
     on a maker's page, the maker. Not the rail — a visitor has no library to navigate. It reuses
     `.brand` rather than restyling it, which is why it lives here beside it and not in
     pages/public.css: that sheet is about page CONTENT, and a `pages`-layer rule would outrank
     `shell` and start a cascade fight over the wordmark.
     IT NEEDS NO `.brand-name` OVERRIDE, and neither does any `.brand` outside the rail: the icon
     rail is a container query on `.rail` (below), which matches the rail's descendants and nothing
     else. Until F-S20-1 the ≤1099px copy of those rules was unscoped, so this header and the
     landing each restored their wordmark and the sign-in card, which did not, lost it. --- */
  .public-header { display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
                   max-width: 1360px; margin: 0 auto; padding: 10px 16px;
                   border-bottom: 1px solid var(--line); }
  .public-header .public-author { color: var(--fg-muted); font-size: 12px; }
  .public-header .public-intro { margin: 0; font-size: 12px; }

  .rail-nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
  .rail-link {
    position: relative; display: flex; align-items: center; gap: 10px; min-height: 34px; padding: 0 10px;
    border-radius: var(--radius-control); color: var(--fg-muted); font-weight: 500; white-space: nowrap;
  }
  .rail-link:hover { color: var(--fg); background: var(--raised); text-decoration: none; }
  /* The lit link is INK, never accent: the accent is the one action. The marker sits on the rail's
     edge, -8px being the rail's own padding-inline (8px in every state, collapsed included). */
  .rail-link[aria-current="page"] { color: var(--fg); background: var(--surface); box-shadow: inset 0 0 0 1px var(--line); }
  .rail-link[aria-current="page"]::before { content: ""; position: absolute; left: -8px; top: 8px; bottom: 8px; width: 3px; border-radius: 0 2px 2px 0; background: var(--fg); }
  .rail-icon { width: 16px; height: 16px; flex: 0 0 auto; }

  /* The places sidebar's groups (structural redesign, slice 2). A group head is a LABEL: Plex Sans,
     uppercase (cohesion decision 4). Project links carry no icon and sit indented under their head;
     a pin carries the folder glyph. The foot nav takes the auto margin that pushes it and the
     account foot to the bottom. */
  .rail-group-head { margin: var(--space-3) 10px 2px; font: 600 11px/16px var(--font-ui); text-transform: uppercase; letter-spacing: .08em; color: var(--fg-muted); }
  .rail-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .rail-project { min-height: 30px; padding-left: 36px; font-weight: 400; }
  .rail-pin, .rail-tool-pin { font-weight: 400; }
  .rail-nav--foot { margin-top: auto; }

  .rail-foot { margin-top: 0; display: flex; flex-direction: column; gap: 2px; padding: var(--space-2) 0 0; border-top: 1px solid var(--line); }
  .rail-foot form { margin: 0; }
  .rail-btn {
    display: inline-flex; align-items: center; gap: 10px; width: 100%; min-height: 30px; padding: 0 10px;
    background: none; border: 0; border-radius: var(--radius-control); color: var(--fg-muted);
    font: 500 13px/1 var(--font-ui); text-align: left; cursor: pointer;
  }
  /* text-decoration: none because the Settings link (an <a>) also carries .rail-btn -- the other
     three rail-foot controls are <button>s, which never picked up base.css's `a:hover` underline. */
  .rail-btn:hover { color: var(--fg); background: var(--raised); text-decoration: none; }
  /* The Settings link's lit state, on the hub and its own children (settings/tags) -- mirrors
     .rail-link[aria-current="page"]'s ink-on-surface; it takes no edge marker, which belongs to the
     rail's section links alone. */
  .rail-foot .rail-btn[aria-current="page"] { color: var(--fg); background: var(--surface); }
  .rail-user { padding: 4px 10px; font: 400 12px/16px var(--font-mono); color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* THE ICON RAIL, ONCE (F-S20-1): icons only, whichever trigger narrowed the rail, the collapse
     cookie (`body.app-shell.rail--collapsed`, above) or a viewport below 1100px (the media block
     below). Both set only the grid track; this asks the rail's own content width (39px narrow, 199px
     wide, so any threshold between serves), so it matches the rail's descendants and nothing else.
     It was two hand-synced copies, a `.rail--collapsed`-prefixed one and an UNSCOPED one in the
     ≤1099px media query, and every rail edit landed twice. css_hygiene_test.rb refuses a second
     copy; shell_test.rb draws both triggers and the sign-in card's wordmark. */
  @container rail (max-width: 120px) {
    .brand { justify-content: center; padding: 4px 0; }
    .brand-name, .rail-label, .rail-user, .rail-btn span,
    .rail-group-head, .rail-project, .rail-pin, .rail-tool-pin { display: none; }
    .rail-link, .rail-btn { justify-content: center; padding-inline: 0; }
    /* The lit project, pin or pinned tool just hid, so its family link (ShellHelper#sidebar_family_lit?) wears the
       lit look instead; otherwise the icon rail shows nothing lit. */
    .rail-family-lit { color: var(--fg); background: var(--surface); box-shadow: inset 0 0 0 1px var(--line); }
    .rail-family-lit::before { content: ""; position: absolute; left: -8px; top: 8px; bottom: 8px; width: 3px; border-radius: 0 2px 2px 0; background: var(--fg); }
  }

  .shell-main { min-width: 0; display: flex; flex-direction: column; }
  .topbar {
    position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: var(--space-4);
    min-height: var(--topbar-height); padding: 0 var(--space-5); background: var(--bg); border-bottom: 1px solid var(--line);
  }
  .topbar-crumbs { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; }
  .topbar-title { font-weight: 600; }
  .topbar-search {
    display: flex; align-items: center; gap: var(--space-2); flex: 0 1 340px; min-width: 160px; height: var(--control-height); padding: 0 10px;
    border: 1px solid var(--line-strong); border-radius: var(--radius-control); background: var(--surface); color: var(--fg-muted);
  }
  .topbar-search:focus-within { border-color: var(--fg); outline: 2px solid var(--focus); outline-offset: 1px; }
  .topbar-search input { flex: 1; min-width: 0; min-height: 0; padding: 0; border: 0; background: transparent; color: var(--fg); font: 400 14px/20px var(--font-ui); }
  .topbar-search input:focus-visible { outline: none; }
  .topbar-search input::-webkit-search-cancel-button { filter: grayscale(1); }
  /* New ▾ sits at the bar's right edge at every width. On a phone the bar wraps and New takes a row
     of its own; without this it wrapped to the LEFT and its right-anchored menu opened at x≈-185,
     off-screen (found in slice 2's Task 3; test/system/new_menu_test.rb measures it at 375px). */
  .topbar-new { margin-left: auto; }
  /* THE PHONE'S "+" AND ITS UPLOAD ITEM (layouts/_topbar.html.erb) are hidden here and shown in the
     ≤719px block. THE LAYER ORDER, NOT SPECIFICITY, is what lets this beat `.menu-item`'s own `display:
     flex`: `shell` comes after `components`, so a single class here wins over any selector there.
     test/system/phone_topbar_test.rb asserts it by visibility at desktop width. */
  .topbar-new-plus, .topbar-new-upload { display: none; }
  /* THE TOP BAR'S UPLOAD (layouts/_topbar_upload.html.erb): the button, then where it saves. The
     caption is the answer to "where will this go", so its folder is ink, not muted; below 1100px it
     hides and the button's title carries it. */
  .topbar-upload { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
  .topbar-upload .uploader { gap: var(--space-2); }
  .topbar-upload-caption { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: var(--fg-muted); }
  .topbar-upload-caption strong { color: var(--fg); font-weight: 600; }
  .topbar-kbd { padding: 2px 5px; border: 1px solid var(--line); border-radius: 2px; font: 500 11px/1 var(--font-mono); color: var(--fg-muted); }

  main.container { width: 100%; max-width: var(--container-max); margin: 0 auto; padding: var(--space-5) var(--space-5) var(--space-8); }
  main.container.container-wide { max-width: none; }

  .flash-stack {
    position: fixed; top: 12px; right: 12px; z-index: 900; display: flex; flex-direction: column; gap: 8px;
    width: min(420px, calc(100vw - 24px)); pointer-events: none;
  }
  /* A floating toast is OPAQUE: the -soft tint is translucent, so it layers over --surface rather
     than letting the page show through. */
  .flash-stack > .flash { box-shadow: var(--shadow-float); pointer-events: auto; background-color: var(--surface); background-image: linear-gradient(var(--ok-soft), var(--ok-soft)); }
  .flash-stack > .flash-alert { background-image: linear-gradient(var(--danger-soft), var(--danger-soft)); }
  .flash-stack:empty { display: none; }

  .tabbar { display: none; }

  /* Signed-out pages: the auth card (Task 5 writes the markup). */
  /* QUALIFIED `main.container.auth-main`, NOT a bare `.auth-main`, and the qualifier is the whole
     rule. Both live in this layer, so specificity decides, and `main.container` (0,1,1) outranks a
     bare `.auth-main` (0,1,0) — so from the day this shipped the auth card computed max-width
     1360px and padding-top 20px, i.e. neither declaration here ever applied and every sign-in and
     sign-up page rendered full width. `container-wide` three lines up is the same shape written
     correctly and is why that one has always worked. Measured on /users/sign_up at 1440px; guarded
     by test/system/shell_test.rb, which reads the COMPUTED value — a static check on this file
     cannot see a specificity loss. */
  main.container.auth-main { max-width: 440px; padding-top: var(--space-7); }
  .auth-card { padding: var(--space-5); border: 1px solid var(--line); border-radius: var(--radius-panel); background: var(--surface); }
  .auth-card .brand { padding: 0; margin-bottom: 18px; }
  .auth-card h2 { font-size: 18px; margin: 0 0 16px; }
  .auth-card .field input { max-width: none; }
  .auth-card .actions { margin-top: 8px; }
  .auth-hint { font-size: 12px; color: var(--fg-muted); }
  .auth-links { list-style: none; margin: 16px 0 0; padding: 12px 0 0; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 6px; font-size: 12px; }
  #error_explanation {
    margin: 0 0 14px; padding: 10px 12px; border-radius: var(--radius-control);
    border: 1px solid var(--danger); background: var(--danger-soft); color: var(--fg); font-size: 13px;
  }
  #error_explanation h2 { font-size: 13px; margin: 0 0 4px; }
  #error_explanation ul { margin: 0; padding-left: 18px; }
  .account-delete { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--line); }

  @media (max-width: 1099px) {
    /* The narrow viewport's half of the icon rail: the TRACK, whatever the cookie says. */
    body.app-shell { grid-template-columns: var(--rail-collapsed-width) minmax(0, 1fr); }
    .topbar { padding: 0 16px; }
    .topbar-upload-caption { display: none; }
    main.container { padding: 16px 16px 64px; }
  }

  @media (max-width: 719px) {
    body.app-shell { grid-template-columns: minmax(0, 1fr); padding-bottom: var(--tabbar-height); }
    .rail { display: none; }
    .tabbar {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; display: flex;
      height: var(--tabbar-height); background: var(--surface); border-top: 1px solid var(--line);
    }
    .tabbar-link { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; color: var(--fg-muted); font-size: 11px; }
    .tabbar-link:hover { text-decoration: none; }
    .tabbar-link[aria-current="page"] { color: var(--fg); font-weight: 600; }
    .topbar { flex-wrap: wrap; gap: 6px 8px; padding: 6px 12px; }
    /* THE PHONE ROW IS THE BREADCRUMB AND THE "+" (spec §4.3), with search on a full-width row below
       (slice 7). `order` moves only the search box; the crumbs, New and Upload keep their DOM order. */
    .topbar-search { flex: 1 1 100%; min-width: 0; order: 1; }
    /* ON A PHONE THE NEW MENU SPANS THE BAR rather than hanging off New's right edge. Since slice 4
       put Upload to New's right, a menu anchored to New opened ~14px off the left edge of a 360px
       phone (and 1px off CI's 375px, whose Linux Chrome spends 15 of them on a scrollbar). A static
       `.topbar-new` hands the absolutely positioned list to `.topbar`, which is sticky and therefore
       its containing block, so the list sits under the bar between the gutters wherever New lands.
       test/system/new_menu_test.rb measures it at 375 and 360. */
    .topbar-new { position: static; }
    .topbar-new .menu-list { left: 12px; right: 12px; min-width: 0; }
    /* ONE "+" FOR NEW AND UPLOAD (structural redesign spec §4.3, slice 7). The summary reads "+", and
       the top bar's Upload moves inside it as the menu's first item, which clicks the hidden LABEL
       (upload_forward_controller.js) so a page with no folder still asks with the chooser. Only the
       LABEL hides, never `.topbar-upload`: the chunk flow's `.upload-status` beside it is where an
       upload started from the menu reports its progress. `revert-layer` hands the item back the
       display its own component gives it (`.menu-item`'s flex, the separator's block) rather than
       restating either here. */
    .topbar-new-label { display: none; }
    .topbar-new-plus { display: inline; }
    .topbar-new-upload { display: revert-layer; }
    .topbar-upload label.upload-btn { display: none; }
    .topbar-kbd { display: none; }
    main.container { padding: 12px 12px 72px; }
    .page-head h1 { font-size: 18px; }
  }
}
