/*
 * Centgenie's shell, served at `/elements/site-layout.css`.
 *
 * The palette is restated here rather than read from `/assets/styles/centgenie.css`: that sheet
 * is linked by the centgenie views, and the shell also wraps views that never link it — the
 * account pages, anything core routes through `site-layout`. Declaring the tokens here is what
 * makes the chrome one surface across all of them; a view that declares the same values again on
 * `.cg` costs nothing.
 *
 * They land on the document as well as on the element, because the document paints with them
 * too, and a token declared only on the shell is undefined by the time `body` asks for it —
 * which drops the declaration rather than inheriting anything.
 */
site-layout,
body:has(site-layout) {
  --cg-bg: #0a0a0a;
  --cg-raise: #161616;
  --cg-fg: #fcfcfc;
  --cg-muted: #8a8a8a;
  --cg-line: #242424;
  --cg-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --cg-duration: 140ms;
}

/*
 * Committed dark from the root down: the viewport's own scrollbar takes its colour from the
 * document element, and a light scrollbar running down the side of a black page is the one piece
 * of chrome the app cannot paint for itself.
 */
:root:has(site-layout) {
  color-scheme: dark;
}

/* Overscroll, and any strip the shell does not reach, are the document's to paint. Scoped to
   pages that mount the shell, because the router leaves stylesheets loaded after navigating away
   from the view that asked for them. */
body:has(site-layout) {
  background: var(--cg-bg);
  color: var(--cg-fg);
}

site-layout {
  /*
   * The one number the views need from the shell. They subtract it from the viewport to size
   * their own column, so a screen fills exactly what is left under the bar rather than adding a
   * bar's worth of scroll to every page.
   */
  --topbar-height: 3.5rem;

  color-scheme: dark;
  display: block;
  min-height: 100dvh;
  background: var(--cg-bg);
  color: var(--cg-fg);
}

site-layout .site {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

/* Top bar
   ------------------------------------------------------------------------- */

/*
 * Sticky rather than fixed: the bar is in the flow, so nothing underneath has to reserve space
 * for it, and it is still there on the way down a long ledger. The horizontal padding grows
 * with the window so the bar stays full-bleed while its contents stop at a readable width.
 */
/*
 * Three columns, and the middle is centred on the window rather than between its neighbours: two
 * equal `1fr` sides put it there no matter that the side with the buttons holds more than the
 * side with the mark.
 *
 * `min-content` as the floor rather than `0` is what makes that safe at any width. A side may
 * shrink to share the space evenly, but never below what it is holding — so when the window is
 * too narrow for the middle to be centred and clear of both sides, the sides stop at their
 * content, the middle drifts by the difference, and nothing lands on top of anything. Centred
 * whenever it fits, as centred as it can be when it does not, with no width written down.
 */
site-layout .site-topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: minmax(min-content, 1fr) auto minmax(min-content, 1fr);
  align-items: center;
  gap: 0.75rem;
  height: var(--topbar-height);
  padding-inline: max(1rem, calc((100% - 86rem) / 2));
  border-bottom: 1px solid var(--cg-line);
  background: color-mix(in srgb, var(--cg-bg), transparent 20%);
  backdrop-filter: blur(12px);
}

site-layout .site-topbar-group {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

/*
 * Placed by column rather than left to auto-placement, which hands an absent item's column to
 * the next one along — that is how the account menu ended up in the middle of the bar on a page
 * with nothing to put there. Stated columns cannot drift.
 */
site-layout .site-topbar > .site-topbar-group:first-child {
  grid-column: 1;
}

site-layout .site-topbar-controls {
  grid-column: 2;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The shell's own chrome sits at the far end; the screen's buttons come before it. */
site-layout .site-topbar > .site-topbar-group:last-child {
  grid-column: 3;
  justify-content: flex-end;
}

site-layout .site-topbar-actions {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

/*
 * Text chrome, for a screen whose bar carries links rather than icon buttons — the landing page's
 * own navigation, which stands in for the app's menu while it is up.
 */
site-layout .site-topbar-nav {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  min-width: 0;
}

site-layout .site-topbar-nav a:not(.site-topbar-action) {
  padding: 0.4rem 0.7rem;
  border-radius: 999px;
  color: var(--cg-muted);
  font-size: 0.9rem;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--cg-duration) var(--cg-ease),
    color var(--cg-duration) var(--cg-ease);
}

site-layout .site-topbar-nav a:not(.site-topbar-action):hover {
  background: color-mix(in srgb, var(--cg-fg), transparent 92%);
  color: var(--cg-fg);
}

site-layout .site-topbar-action {
  flex: none;
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  background: var(--cg-fg);
  color: var(--cg-bg);
  font-size: 0.85rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--cg-duration) var(--cg-ease),
    transform var(--cg-duration) var(--cg-ease);
}

site-layout .site-topbar-action:hover {
  background: color-mix(in srgb, var(--cg-fg), #fff 25%);
}

site-layout .site-topbar-action:active {
  transform: scale(0.97);
}

site-layout .site-topbar-nav a:focus-visible {
  outline: 2px solid var(--cg-fg);
  outline-offset: 2px;
}

/*
 * A screen whose own chrome replaces the app's: the menu would only repeat what its links
 * already offer, so it stands down while the screen's own is up.
 */
site-layout .site-topbar[exclusive] .site-menu-host {
  display: none;
}

/* Below this the two anchors are conveniences; the one that matters is the app. */
@media (max-width: 34rem) {
  site-layout .site-topbar-nav a:not(.site-topbar-action) {
    display: none;
  }
}

/*
 * The other thing a screen can put in the middle: its name, for a screen whose bar has no switch
 * to hold. It is the page's real heading, hoisted — a screen that says what it is up here has no
 * reason to say it again in a row of its own underneath.
 */
site-layout .site-topbar-title {
  margin: 0;
  overflow: hidden;
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* An empty region is not a gap in the bar. */
site-layout .site-topbar-controls:empty,
site-layout .site-topbar-actions:empty {
  display: none;
}

/* Menu
   ------------------------------------------------------------------------- */

/*
 * The round button is the app's own — same size, same press, same focus ring as the actions a
 * screen puts beside it — so the shell's menu does not announce itself as a different kind of
 * control from the ones the page owns.
 */
site-layout .site-menu-host {
  position: relative;
  display: flex;
}

site-layout .site-menu-button {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--cg-raise);
  color: var(--cg-fg);
  font: inherit;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition:
    background-color var(--cg-duration) var(--cg-ease),
    transform var(--cg-duration) var(--cg-ease);
}

site-layout .site-menu-button:hover,
site-layout .site-menu-button[aria-expanded='true'] {
  background: color-mix(in srgb, var(--cg-fg), transparent 82%);
}

site-layout .site-menu-button:active {
  transform: scale(0.92);
}

/*
 * The three faces of the same button. The dots are type and the other two are drawn, so they are
 * sized here rather than left to the font — an icon that changes size when the mode changes reads
 * as the button moving.
 */
site-layout .site-menu-glyph {
  display: block;
  line-height: 1;
}

site-layout .site-menu-button svg.site-menu-glyph {
  width: 1.05rem;
  height: 1.05rem;
  fill: none;
}

site-layout .site-menu-button [hidden] {
  display: none;
}

/* Syncing is the state worth noticing, so it is the only one the button says out loud. */
site-layout .site-menu-button[storage='cloud'] {
  color: var(--cg-fg);
  background: color-mix(in srgb, var(--cg-fg), transparent 84%);
}

site-layout .site-menu-button:focus-visible,
site-layout .site-menu-item:focus-visible {
  outline: 2px solid var(--cg-fg);
  outline-offset: 2px;
}

site-layout .site-menu {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 13rem;
  padding: 0.4rem;
  border: 1px solid var(--cg-line);
  border-radius: 0.9rem;
  background: var(--cg-raise);
  box-shadow: 0 12px 32px rgb(0 0 0 / 0.45);
}

/*
 * Two kinds of element in here do not get hidden by the attribute alone: one that sets its own
 * `display` outranks the UA rule, and an SVG is not covered by it at all. Both arrive — the menu
 * is display:flex, and the ledger hoists a button holding two icons with one of them hidden — so
 * the shell states it for everything under it, the way `.cg` already does for a screen.
 */
site-layout [hidden] {
  display: none !important;
}

/* The gates render as a wrapper element; their items have to sit in the menu's own column. */
site-layout .site-menu unauth-gate,
site-layout .site-menu auth-gate,
site-layout .site-menu admin-gate,
site-layout .site-menu-page {
  display: contents;
}

/*
 * Items are a mix of links and buttons — a destination and a choice read the same in a menu — so
 * the alignment a `button` gets from the UA has to be overridden, or every choice sits indented
 * from every destination above it.
 */
site-layout .site-menu-item {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.5rem;
  padding: 0.5rem 0.6rem;
  border: 0;
  border-radius: 0.6rem;
  background: transparent;
  color: var(--cg-fg);
  font: inherit;
  font-size: 0.9rem;
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--cg-duration) var(--cg-ease);
}

site-layout .site-menu-item:hover,
site-layout .site-menu-item:focus-visible {
  background: color-mix(in srgb, var(--cg-fg), transparent 90%);
  outline: none;
}

/* Where you already are, said the way the app says it everywhere else. */
site-layout .site-menu-item[active] {
  background: color-mix(in srgb, var(--cg-fg), transparent 84%);
}

/*
 * One column for the glyph and one for the label, so items line up whether their mark is an
 * icon, a tick, or nothing at all.
 */
site-layout .site-menu-icon,
site-layout .site-menu-tick {
  flex: none;
  width: 1rem;
  text-align: center;
  color: var(--cg-muted);
}

site-layout .site-menu-tick {
  color: var(--cg-fg);
  opacity: 0;
  transition: opacity var(--cg-duration) var(--cg-ease);
}

site-layout .site-menu-item[aria-checked='true'] .site-menu-tick {
  opacity: 1;
}

site-layout .site-menu-label {
  margin: 0;
  padding: 0.35rem 0.6rem 0.2rem;
  color: var(--cg-muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

site-layout .site-menu-rule {
  margin: 0.3rem 0.4rem;
  border: 0;
  border-top: 1px solid var(--cg-line);
}

/* Shortcuts
   ------------------------------------------------------------------------- */

/* The key beside the menu item that opens the list — how the shortcut is discovered at all. */
site-layout .site-keys-hint {
  margin-left: auto;
}

/*
 * The box is written out here rather than borrowed from `.cg-modal`: this dialog is the shell's,
 * and the shell is on pages that do not load a screen's stylesheet. A `<dialog>` centres itself
 * with the UA's `margin: auto`, which the site's reset zeroes out — restating it is what puts it
 * back in the middle.
 *
 * Wider than the app's other modals: two columns of reading on a laptop, which is where a
 * keyboard shortcut is used at all, and one column on a phone, where it is only ever read.
 */
site-layout .site-keys {
  position: fixed;
  inset: 0;
  width: min(34rem, calc(100vw - 2rem));
  max-height: min(40rem, calc(100dvh - 2rem));
  margin: auto;
  padding: 1.25rem;
  overflow-y: auto;
  border: 1px solid var(--cg-line);
  border-radius: 1.25rem;
  background: var(--cg-raise);
  color: var(--cg-fg);
  color-scheme: dark;
}

site-layout .site-keys::backdrop {
  background: rgb(0 0 0 / 0.6);
  backdrop-filter: blur(2px);
}

site-layout .site-keys-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

site-layout .site-keys-head h2 {
  margin: 0;
  margin-right: auto;
  font-size: 1.05rem;
  font-weight: 500;
}

site-layout .site-keys-groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1.25rem;
}

site-layout .site-keys-group h3 {
  margin: 0 0 0.4rem;
  color: var(--cg-muted);
  font-size: 0.75rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

site-layout .site-keys-group dl {
  margin: 0;
  display: flex;
  flex-direction: column;
}

/*
 * Each row is a term and its keys, which is what the wrapping `div` in the markup is for — a
 * `dl` cannot put a `dt` and its `dd` on one line without one.
 */
site-layout .site-keys-group dl > div {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.35rem 0;
}

site-layout .site-keys-group dt {
  flex: 1;
  min-width: 0;
  font-size: 0.9rem;
}

site-layout .site-keys-group dd {
  display: flex;
  flex: none;
  gap: 0.25rem;
  margin: 0;
}

/* A key, drawn as one. Monospace digits so `1 2 3 4` is a row of squares rather than a stagger. */
site-layout kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  padding: 0.15rem 0.35rem;
  border: 1px solid var(--cg-line);
  border-bottom-width: 2px;
  border-radius: 0.4rem;
  background: color-mix(in srgb, var(--cg-fg), transparent 92%);
  color: var(--cg-fg);
  font-family: inherit;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
}

site-layout .site-keys-or {
  color: var(--cg-muted);
  font-size: 0.75rem;
}

site-layout .site-keys-note {
  margin: 1.25rem 0 0;
  color: var(--cg-muted);
  font-size: 0.8rem;
}

/* Viewport
   ------------------------------------------------------------------------- */

/*
 * The document scrolls, not a box inside it — that is what the router assumes when it scrolls
 * to the top after a navigation, and what a desktop browser assumes when it restores a position
 * or hands the page to a trackpad. The screen fills the width and centres its own column.
 */
site-layout .site-view {
  display: flex;
  flex: 1;
  justify-content: center;
  min-width: 0;
}

site-layout .site-view > * {
  flex: 1;
  min-width: 0;
  max-width: 100%;
}

/* Phone
   ------------------------------------------------------------------------- */

/*
 * A mark, a switch and three buttons across 320px. They fit by giving up the padding they can
 * afford, and every pixel handed back here is one the middle can use before it has to give up
 * being centred.
 */
@media (max-width: 30rem) {
  site-layout .site-topbar {
    gap: 0.4rem;
    padding-inline: 0.6rem;
  }

  site-layout .site-topbar-group {
    gap: 0.4rem;
  }

  site-layout .site-topbar-controls .cg-segment {
    padding: 0.4rem 0.55rem;
    font-size: 0.8rem;
  }

  site-layout .site-topbar-actions {
    gap: 0.15rem;
  }

  site-layout .site-topbar-actions .cg-round,
  site-layout .site-menu-button {
    width: 2rem;
    height: 2rem;
    font-size: 1rem;
  }
}

/* Desktop
   ------------------------------------------------------------------------- */

@media (min-width: 48rem) {
  site-layout {
    --topbar-height: 4rem;
  }

  site-layout .site-topbar {
    gap: 1rem;
    padding-inline: max(1.5rem, calc((100% - 86rem) / 2));
  }
}
