/* ==========================================================================
   EZUI Shell (ezui-shell)

   The frame every page sits in: navigation, a heading line, and the region
   that holds the page. Seven areas, two of which stretch.

   The whole point of this file is that the numbers at the top of the screen
   are worked out from one another rather than typed next to each other. See
   "The top line" in the guide. If you are about to type a pixel value into
   this file, check first whether it can be derived from --ezui-shell-top-line
   instead, because the bug this component exists to stop is two numbers that
   have to agree and do not.

   Colour comes from the palette in ezui-colour.css, never a hex here
   (Foundations rule 9).
   ========================================================================== */

.ezui-shell {
  /* ---- the numbers a page may change ---- */
  --ezui-shell-top-line: 30px;
  --ezui-shell-gutter: 32px;
  --ezui-shell-nav-width: 220px;
  --ezui-shell-nav-pad: 8px;
  --ezui-shell-gap: 20px;
  --ezui-shell-body-end: 48px;

  /* ---- what the screen says it needs ----
     A phone with a notch, an island or a home bar reports how much room to
     leave at each of its edges, and the curve of its corners is inside that.
     Every other screen reports nothing, so these come out 0 and the same
     rules serve a desktop.

     Tokens rather than env() written into each rule, because nothing can
     make a browser report an inset on demand. Written directly, these rules
     would be the only part of the shell that nothing could ever check. As
     tokens, a check sets them to a number and watches the layout answer. */
  --ezui-shell-safe-top: env(safe-area-inset-top, 0px);
  --ezui-shell-safe-bottom: env(safe-area-inset-bottom, 0px);
  --ezui-shell-safe-left: env(safe-area-inset-left, 0px);
  --ezui-shell-safe-right: env(safe-area-inset-right, 0px);

  /* ---- what the top line is made of ---- */
  --ezui-shell-brand-size: 28px;
  --ezui-shell-toggle-size: 36px;
  --ezui-shell-head-line: 30px;

  /* ---- worked out, not typed ----
     The band is what puts everything on the line: twice the top line tall,
     with its contents centred, so whatever is in it lands on the line at
     whatever size. The button is worked out instead, being the only thing
     at the top that is not in a band. */
  --ezui-shell-band: calc(var(--ezui-shell-top-line) * 2);
  --ezui-shell-toggle-inset: calc(var(--ezui-shell-top-line) - var(--ezui-shell-toggle-size) / 2);

  display: flex;
  /* Exactly the viewport, and nothing outside the body ever scrolls. dvh
     rather than vh so a phone's collapsing browser bars do not leave the
     shell taller than what can be seen. */
  height: 100dvh;
  overflow: hidden;
  /* Both columns come in off a notch. The two things that float over the
     page are fixed, so they do not move with this and say so themselves. */
  padding-left: var(--ezui-shell-safe-left);
  padding-right: var(--ezui-shell-safe-right);
}

/* ---- the navigation ------------------------------------------------------ */

.ezui-shell__nav {
  width: var(--ezui-shell-nav-width);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding-inline: var(--ezui-shell-nav-pad);
  background: var(--ezui-panel);
  border-right: 1px solid var(--ezui-border);
}

/* A band, so whatever is in it lands on the top line. min-height rather than
   height: something taller than the band grows it rather than spilling out of
   it, and then the line moves, which is the right way round to be wrong. */
.ezui-shell__nav-head {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  /* The band is 60px of room, with the border and the screen's own inset
     outside it. Said out loud because a project that sets
     * { box-sizing: border-box }, which most do, would otherwise have the
     band eat its own border and padding and come out 59. EZY DNS did
     exactly that: its foot measured 59 where the demo site's measured 60,
     from the same stylesheet. */
  box-sizing: content-box;
  min-height: var(--ezui-shell-band);
  padding-top: var(--ezui-shell-safe-top);
}
.ezui-shell__brand {
  width: var(--ezui-shell-brand-size);
  height: var(--ezui-shell-brand-size);
  flex-shrink: 0;
  border-radius: 6px;
  object-fit: contain;
}
.ezui-shell__name {
  font-size: var(--ezui-type-m);
  font-weight: var(--ezui-type-semibold);
  color: var(--ezui-text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The links. The only part of the navigation that can outgrow the screen, so
   it is the only part that scrolls. min-height: 0 is what lets it: without
   it a flex item refuses to shrink below its content and the whole column
   grows instead. */
.ezui-shell__nav-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* The same band as the two at the top, so the bottom of the navigation
   weighs what its head does and the border lands on a number the shell
   already uses. Plus whatever the screen wants below it. */
.ezui-shell__nav-foot {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  box-sizing: content-box;
  min-height: var(--ezui-shell-band);
  padding-bottom: var(--ezui-shell-safe-bottom);
  border-top: 1px solid var(--ezui-border);
}

/* ---- the page ----------------------------------------------------------- */

.ezui-shell__main {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* The same band. This is the one a page puts things in beside the heading,
   and the reason the band is a band: a 36px button in a row sized to a 30px
   heading pushes the heading 3px down the screen, and it did. */
.ezui-shell__head {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  box-sizing: content-box;
  min-height: var(--ezui-shell-band);
  padding-top: var(--ezui-shell-safe-top);
  padding-inline: var(--ezui-shell-gutter);
}
/* Whatever the page puts in the heading row sits at the far end and centres
   on the heading, whatever the heading's length. */
.ezui-shell__head > :first-child { margin-right: auto; }
.ezui-shell__head h1 {
  margin: 0;
  /* The largest text size there is. A page's title is the one piece of text
     on the screen that says where you are, and at xl it read as a heading
     inside the page rather than as the name of it. The line box below is
     bigger than xxl needs on purpose: the band centres whatever is in it, so
     the extra is air around the title rather than a push downwards. */
  font-size: var(--ezui-type-xxl);
  line-height: var(--ezui-shell-head-line);
  font-weight: var(--ezui-type-semibold);
  color: var(--ezui-text-primary);
  min-width: 0;
}

/* Nothing on top: the band above already leaves half of itself below the
   heading, which is the gap. Adding more here would be the same space
   written twice.

   The end is its own number rather than the gutter, which it used to
   borrow. A region that scrolls needs more room at its end than at its
   sides: there is no edge of a page beyond it, so the last line sits on the
   bottom of the screen and reads as cut off. At the sides there is a real
   edge a few pixels away and the same space looks right. */
.ezui-shell__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 0 var(--ezui-shell-gutter)
           calc(var(--ezui-shell-body-end) + var(--ezui-shell-safe-bottom));
}

/* ---- the two regions that scroll ----------------------------------------
   EZY UI Scroll wraps a watched box in a .ezui-scroll__holder, so once its
   JS has run the holder is the flex child and the region is inside it. Both
   are named here, so the layout is the same before and after that happens
   and the same whether or not the scroll component is loaded at all. */
.ezui-shell__main > .ezui-shell__body,
.ezui-shell__main > .ezui-scroll__holder,
.ezui-shell__nav > .ezui-shell__nav-body,
.ezui-shell__nav > .ezui-scroll__holder {
  flex: 1 1 auto;
  min-height: 0;
}
/* border-box, and it matters: nothing in EZUI sets box-sizing, so the
   default is content-box, and height: 100% with padding on the same element
   makes the box taller than the holder by exactly its padding. The shell
   clips, so that much of the region hangs below the bottom of the screen
   where nobody can see it. The padding at the end of a page was doing
   precisely that, which is why setting it to 48px changed nothing you could
   look at. */
.ezui-scroll__holder > .ezui-shell__body,
.ezui-scroll__holder > .ezui-shell__nav-body {
  height: 100%;
  box-sizing: border-box;
}

/* ---- the menu button and the scrim: only on a narrow screen -------------- */

.ezui-shell__toggle,
.ezui-shell__nav-close { display: none; }

/* Fixed, so the shell's own padding does not move it and it has to take the
   insets itself. */
.ezui-shell__toggle {
  position: fixed;
  top: calc(var(--ezui-shell-toggle-inset) + var(--ezui-shell-safe-top));
  left: calc(var(--ezui-shell-toggle-inset) + var(--ezui-shell-safe-left));
  z-index: 230;
  width: var(--ezui-shell-toggle-size);
  height: var(--ezui-shell-toggle-size);
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: none;
  color: var(--ezui-text-primary);
  cursor: pointer;
}

.ezui-shell__nav-close {
  margin-left: auto;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: none;
  color: var(--ezui-text-primary);
  cursor: pointer;
}

.ezui-shell__scrim { display: none; }

/* ==== narrow ==============================================================
   900px is written here and once more below, and it is the one number in
   this file that cannot be a token: a media query cannot read a custom
   property. See "The one number that cannot be a token" in the guide.
   ======================================================================== */
@media (max-width: 900px) {
  .ezui-shell {
    --ezui-shell-gutter: 16px;
  }

  .ezui-shell__toggle,
  .ezui-shell__nav-close { display: inline-flex; }

  /* Fixed too, so the same applies: it starts at the very edge of the
     screen and brings its contents in off the notch itself. */
  .ezui-shell__nav {
    position: fixed;
    top: 0;
    left: 0;
    height: 100dvh;
    padding-left: calc(var(--ezui-shell-nav-pad) + var(--ezui-shell-safe-left));
    z-index: 220;
    transform: translateX(-100%);
    transition: transform .18s ease;
    box-shadow: 0 8px 24px rgba(var(--ezui-ink), .16);
  }
  .ezui-shell[data-nav-open] .ezui-shell__nav { transform: none; }

  /* Transparent on purpose. It is there to catch a tap outside the drawer,
     and the drawer's own shadow is what separates it from the page. A dim
     over the page was tried and looked heavier than the thing it was
     separating. */
  .ezui-shell__scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 210;
    background: transparent;
    pointer-events: none;
  }
  .ezui-shell[data-nav-open] .ezui-shell__scrim { pointer-events: auto; }

  /* One button for one job: while the drawer is open its own close button
     has taken over, and this one would be floating over the thing it would
     close. */
  .ezui-shell[data-nav-open] .ezui-shell__toggle { display: none; }

  /* Room for the button, worked out rather than measured: where the button
     starts, plus how big it is, plus the gap after it, less the gutter the
     heading already has. */
  .ezui-shell__head {
    padding-left: calc(
      var(--ezui-shell-toggle-inset) + var(--ezui-shell-toggle-size)
      + var(--ezui-shell-gap) - var(--ezui-shell-gutter)
    );
  }
}

@media (prefers-reduced-motion: reduce) {
  .ezui-shell__nav { transition: none; }
}
