/* Shared platform chrome. Screen-specific styles live next door, in screens/. */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

/* The scrollbar must not shift the page. Without this a short screen slides
   sideways relative to a long one — and the header slides with it. */
html {
  scrollbar-gutter: stable;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
}

a {
  color: inherit;
  text-decoration: none;
}

.mono {
  font-family: var(--font-mono);
}

/* ── page skeleton ────────────────────────────────────────────────────────
   There is one card for the whole application, and its width is constant.
   A screen inside it is free to be narrow, but it no longer drags the card
   and the header with it. */

.shell {
  max-width: var(--page-max);
  /* Top and bottom padding: the card is set apart from the window edge and
     ends where the content ends. It deliberately does not grow to the screen's
     height — a surface stretched to the bottom edge would promise content that
     is not there. */
  margin: 20px auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-panel);
  /* `clip` specifically, not `hidden`: `hidden` turns the card into a scroll
     container, and the header inside it stops sticking to the top. */
  overflow: clip;
}

/* The top padding disappears exactly when there is none left on the sides.
   900 is `--page-max`: below it the card is already pressed against the window
   edges, and the background strip above it stops being padding — it becomes
   just a lost line above content that already reaches the edges anyway. The top
   rounded corners are dropped at the same time: a border right at the window's
   edge draws a corner into nowhere.

   A media query cannot read variables, so the number is duplicated here;
   changing `--page-max` means changing this too. */
@media (max-width: 900px) {
  .shell {
    margin-top: 0;
    border-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }
}

/* The header stays in view while scrolling. On the room screen this is not a
   convenience: chrome sliding out of view would mean a disk is alone on screen
   with nothing to tell its drawing from the platform's interface. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* The height is set by the header itself, not by what is inside it.
     Otherwise the header on the landing screen, which has no buttons, sits two
     pixels lower than everywhere else — and the content beneath it jumps on
     every transition. The same disease the screen title bar had, cured the same
     way. */
  min-height: 65px;
  padding: 12px 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.topbar__left {
  display: flex;
  align-items: center;
  gap: 22px;
  min-width: 0;
}

/* The buttons are pressed to the right edge, the name behind them. A separate
   group, because on a phone the buttons move to the row below while the name
   stays on top. */
.topbar__actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
  min-width: 0;
}

/* A header button is not the screen's main action but a standing door. So it
   is smaller and dimmer than the content: it must be at hand without competing
   for attention with what the person came for. */
.topbar__action {
  display: inline-block;
  font-size: 13px;
  color: var(--muted);
  background: #2b2b2b;
  padding: 9px 13px;
  border-radius: var(--r-ui);
  white-space: nowrap;
}

.topbar__action:hover {
  color: var(--text-dim);
}

.topbar__me {
  display: flex;
  align-items: center;
  gap: 9px;
  padding-left: 10px;
  font-size: 14px;
  color: var(--muted);
  min-width: 0;
}

/* ── avatar ──────────────────────────────────────────────────────────────
   The picture is derived from the nickname, so it has neither its own state
   nor its own loading. The backing square stays visible while the picture is
   on its way, and stays forever if a third-party service does not answer: an
   empty square is more honest than a broken-image icon. */
.avatar {
  --avatar-size: 28px;
  width: var(--avatar-size);
  height: var(--avatar-size);
  flex: none;
  background: #3d3d3d;
  border-radius: var(--r-ui);
  overflow: clip;
}

.avatar__img {
  display: block;
  width: 100%;
  height: 100%;
  /* The drawing is pixel art: smoothing it on scale would ruin it. */
  image-rendering: pixelated;
}

/* ── the name in the corner ──────────────────────────────────────────────
   The only thing changeable in a session, so it is edited where it is shown —
   a screen of its own for a single field is not needed. */
.me {
  position: relative;
  display: flex;
  /* A long name must shrink with an ellipsis rather than stretch the header:
     without this zero the chain of flex containers will not let `.nav__me`
     shrink, and on a phone the name together with the panel slid off the
     screen's right edge. */
  min-width: 0;
}

.me__badge {
  display: flex;
  align-items: center;
  gap: 9px;
  font: inherit;
  padding: 4px;
  border: 0;
  border-radius: var(--r-ui);
  background: none;
  color: inherit;
  cursor: pointer;
  min-width: 0;
}

.me__badge:hover {
  background: #2b2b2b;
}

/* The panel hangs beneath the name and never moves the header: were it to
   push the row apart, the very chrome-jumping the header was given a height to
   avoid would return.

   The card clips whatever overflows (`overflow: clip` on `.shell`), so the
   panel must fit inside its height. The application's shortest screen is 404:
   there the card is 343 pixels, and the panel ends at 284. If the panel grows
   noticeably, this needs checking again. */
.me__panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 10;
  width: 300px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-panel);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}

.me__head {
  display: flex;
  align-items: center;
  gap: 12px;
}

.me__headtext {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.me__hint {
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted-3);
}

.me__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The platform's mark. It is also what tells the diskroom interface from a
   picture a disk could have drawn, so it stays in view and stays singular,
   always. */
.brand__mark {
  width: 22px;
  height: 22px;
  flex: none;
  color: var(--text);
}

/* The platform's name is typed in capitals in the markup rather than by
   `text-transform`: that way it both copies correctly and reads to a screen
   reader as a name, not as a word. */
.brand__name {
  font-size: 15px;
  letter-spacing: 0.08em;
  color: var(--text);
}

.nav {
  display: flex;
  gap: 4px;
  align-items: center;
  font-size: 14px;
  color: var(--muted);
  min-width: 0;
}

.nav a {
  padding: 9px 14px;
  border-radius: var(--r-ui);
  white-space: nowrap;
}

.nav a:hover {
  color: var(--text-dim);
}

/* The current section — with a pill, as in the mockup. */
.nav a[aria-current='page'] {
  color: var(--text);
  background: #2b2b2b;
}

/* The screen title bar — the same one across every page.

   Its height is set by itself, not by the content: otherwise a search field
   stretches the bar to 81 pixels, buttons to 75, and bare text settles at 57 —
   and the chrome jumps on every transition, exactly where it promised not to.
   */
.screen__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  min-height: 72px;
  padding: 12px 24px;
  border-bottom: 1px solid var(--border);
}

/* A screen drawn in the mockup as a column centred in the card: room creation
   and the lobby. The card's width does not change here — only the column
   inside it changes, so the header above it stays put.

   The screen title bar is not part of the column: in the mockup its line
   crosses the whole card. */
.screen--narrow > .form,
.screen--narrow > .lobby {
  max-width: 720px;
  margin: 0 auto;
  padding: 40px;
}

/* ── primitives ──────────────────────────────────────────────────────── */

.btn {
  display: inline-block;
  font: inherit;
  font-size: 15px;
  padding: 16px 26px;
  border: 0;
  border-radius: var(--r-ui);
  background: #2e2e2e;
  color: var(--text-dim);
  cursor: pointer;
}

.btn:hover:not(:disabled) {
  background: #363636;
}

.btn--primary {
  background: var(--primary-bg);
  color: var(--primary-fg);
}

.btn--primary:hover:not(:disabled) {
  background: #fff;
}

/* A disabled button must look disabled. There are three of them in the
   application — "Publish" while the header does not parse, "Open room" while
   the request is in flight, and "Run disk" before the hash is checked — and
   until now all three simply stopped responding while staying white: a press
   with no answer reads as broken, not as forbidden.

   The rule comes after the button variants deliberately: their specificity is
   equal, so order decides. Should a new disabled-button variant appear in
   another file, this rule will have to move there too. */
.btn:disabled {
  background: #292929;
  color: var(--muted-3);
  cursor: not-allowed;
}

.btn--sm {
  padding: 9px 18px;
  font-size: 14px;
}

.tag {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  padding: 5px 8px;
  border-radius: var(--r-chip);
  color: var(--muted);
  background: #2b2b2b;
}

.tag--ok {
  color: var(--ok-fg);
  background: var(--ok-bg);
}

.tag--warn {
  color: var(--warn-fg);
  background: var(--warn-bg);
}

.tag--danger {
  color: var(--danger-fg);
  background: var(--danger-bg);
}

.panel {
  background: var(--sunken);
  border: 1px solid var(--border);
  border-radius: var(--r-panel);
}

.section-label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--muted-3);
}

/* Room for the nickname is reserved before it arrives: a session is created by
   a request, and without a reservation the header would jump on every page's
   first load — first empty, then the name pushing its neighbours apart. The
   width in `ch` on a monospace font is exact: `anon_1234` is exactly nine
   characters. */
.nav__me {
  font-size: 14px;
  color: #d4d4d4;
  min-width: 9ch;
  /* And a ceiling: a person picks the name, and without a limit a long one
     ate the navigation sections — "Disks" was truncated before the name was.
     What the user typed is the first thing to give way. The ceiling equals the
     name's own limit (16 characters): whatever is typed fits the header whole,
     and the ellipsis stays insurance against wide letters rather than a
     routine occurrence. */
  max-width: 16ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Two captions for one spot: the short one lives only on a narrow screen. */
.narrow-only {
  display: none;
}

/* "Room:", "Disk:" before the name in the title bar. Deliberately dimmed:
   this is a caption to the name, not part of it, and the name must read
   first. */
.screen__kind {
  color: var(--muted-3);
}
