/*
 * Global light-DOM styles of the dashboard.
 *
 * Convention (taken from the GlassKit-Web SKILL and equally valid here):
 *   · Use GlassKit CLASSES (glass-*, gl-*) in the light DOM only.
 *   · Inside shadow-DOM views use exclusively <glk-*> elements and the --gl-* custom
 *     properties, which do pierce through.
 *   · Colours come from /shared/brand.css, not from here.
 */

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

/*
 * dvh instead of vh: on mobile browsers the viewport grows and shrinks with the address bar,
 * and 100vh leaves an overhang that makes the page scrollable for no reason.
 */
body {
  min-height: 100dvh;
  font-family: var(--gl-font-family);
  color: var(--gl-color-text);
  background: var(--gl-color-bg-dark);
  -webkit-font-smoothing: antialiased;
}

.glass-bg {
  min-height: 100dvh;
}

/* ---- Safe areas of the installed app ------------------------------------------------
 *
 * `viewport-fit=cover` in index.html puts the page underneath the Dynamic Island, the notch
 * and the home indicator. Without the insets below, the header disappears under the island.
 *
 * Two details decide whether this actually works:
 *
 *   · **Additive, not max().** This started as `max(var(--gl-space-lg), env(...-top))`, which
 *     looks reasonable and is wrong: on a Dynamic Island device the inset is around 59px, so
 *     max() returns the inset and the header ends up flush against the island with no
 *     breathing room at all. The normal spacing has to be added ON TOP of the inset.
 *   · **All four sides.** In landscape the cutout moves to the left or right edge, so left and
 *     right count too. There `max()` IS right, so a desktop keeps its normal 16px.
 */
:root {
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: max(var(--gl-space-lg), env(safe-area-inset-left, 0px));
  --safe-r: max(var(--gl-space-lg), env(safe-area-inset-right, 0px));
}

/*
 * The clearance for the floating tab bar comes from GlassKit's
 * .glass-bg--has-tab-bar-floating on the background element; only the home indicator is added
 * here, since that class does not know about safe areas.
 */
app-root {
  display: block;
  padding: calc(var(--gl-space-lg) + var(--safe-t)) var(--safe-r) var(--safe-b) var(--safe-l);
  max-width: 960px;
  margin: 0 auto;
  box-sizing: border-box;
}

/* ---- Floating tab bar ---------------------------------------------------------------
 *
 * The host does the fixed placement, not GlassKit's dock. The dock is set `static` in
 * app-tabbar.js because it centres itself with `transform: translateX(-50%)`, and a transform
 * opens a new backdrop root — after which the bar's backdrop-filter has nothing behind it
 * left to blur. The glass then looks flat while every colour value is still correct, which is
 * what makes the cause so hard to see. Voice-Office-Hub paid for this lesson once already.
 *
 * Centring therefore happens with flexbox, and the bar gets no view-transition-name for the
 * same reason — a name isolates the element just as effectively. It sits outside #app anyway,
 * which is what keeps it still during a view change.
 */
#sj-tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--gl-tab-bar-floating-bottom) + var(--safe-b));
  z-index: 900;
  display: flex;
  justify-content: center;
  /* Landscape: the bar must not slide under the side cutout. */
  padding-inline: var(--safe-l) var(--safe-r);
  box-sizing: border-box;
  /* The strip is full width so it can centre; only the bar itself may catch clicks. */
  pointer-events: none;
}

#sj-tabbar > * {
  pointer-events: auto;
}

/* Icons come out of the light DOM slightly large at the floating size. */
#sj-tabbar glk-tab-item svg {
  width: 22px;
  height: 22px;
}

/*
 * Styles of the shell.
 *
 * Only what lives in the light DOM belongs here: header, sign-in gate, error messages. The
 * views themselves render in the shadow DOM and bring their styles through the .css
 * template of hybrids — GlassKit's global stylesheet does not reach in there, the --gl-*
 * custom properties do.
 *
 * Our own classes here carry the sj- prefix, the way the mockup uses mk-.
 */

/*
 * Header above the views. Empty and invisible while nobody is signed in.
 *
 * `nowrap` on purpose: it used to wrap, and on an iPhone 16 Pro (402px) the brand and the
 * account group came to 162 + 209px in a 362px row — so the header became two rows and pushed
 * the whole page down. The brand gives way instead: it may shrink, and its label truncates.
 * The logo stays, so the header never loses its identity.
 */
.sj-header {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--gl-space-sm);
  margin-bottom: var(--gl-space-lg);
}

.sj-header:empty {
  display: none;
}

.sj-header-account {
  display: flex;
  align-items: center;
  gap: var(--gl-space-sm);
  flex-wrap: wrap;
}

/* Brand mark, doubling as the way back to the dashboard. */
.sj-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--gl-space-xs);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--gl-font-size-md);
  color: var(--gl-color-text);
  min-width: 0;
  flex: 0 1 auto;
}

/* The part that gives way when the row gets tight. */
.sj-brand span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.sj-brand b {
  font-weight: var(--gl-font-weight-semibold);
}

.sj-brand img {
  border-radius: 7px;
  flex: 0 0 auto;
}

.sj-header-account {
  flex: 0 0 auto;
}

/*
 * The account button: avatar only on a phone, avatar plus address from 560px.
 *
 * It deliberately does NOT reuse .sj-icon-btn. It did, and .sj-icon-btn's fixed
 * `width: 34px` sits later in this file — same specificity, so it won the media query and
 * clamped the button to icon width while the address was still inside it. The result was a
 * button showing "m…", which is worse than either of the two intended states. Its own class
 * ends the argument instead of adding an `!important` to it.
 */
.sj-account {
  display: inline-flex;
  align-items: center;
  gap: var(--gl-space-xs);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--gl-font-size-sm);
  color: var(--gl-color-text);
  border-radius: var(--gl-radius-full);
}

.sj-account:focus-visible {
  outline: 2px solid var(--gl-color-primary);
  outline-offset: 3px;
}

.sj-account-mail {
  display: none;
}

@media (min-width: 620px) {
  .sj-account-mail {
    display: inline;
    max-width: 30vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--gl-color-text-muted);
  }
}

.sj-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: var(--gl-radius-full);
  border: 1px solid var(--gl-border-subtle);
  background: transparent;
  color: var(--gl-icon-muted);
  cursor: pointer;
  transition: var(--gl-transition);
}

.sj-icon-btn:hover {
  color: var(--gl-color-text);
}

.sj-icon-btn svg {
  width: 18px;
  height: 18px;
}

/*
 * Offline strip. Warning colour rather than error: no network is a condition, not a fault,
 * and the app is not broken.
 *
 * GlassKit has --gl-color-warning but — unlike error and success — no matching -surface,
 * -border and -on-surface. So the surface comes from --gl-surface-2 and the text stays the
 * normal text colour; inventing tokens with fallbacks would only look like theming while
 * hardcoding a colour.
 */
.sj-connection {
  margin-bottom: var(--gl-space-md);
  padding: var(--gl-space-sm) var(--gl-space-md);
  border-radius: var(--gl-radius-card);
  border: 1px solid var(--gl-color-warning);
  background: var(--gl-surface-2);
  color: var(--gl-color-text);
  font-size: var(--gl-font-size-sm);
  line-height: 1.5;
}

.sj-connection[hidden] {
  display: none;
}

.sj-lead {
  font-size: var(--gl-font-size-lg);
  font-weight: var(--gl-font-weight-semibold);
  margin: 0;
}

/*
 * Toast placement.
 *
 * GlassKit puts `.glass-toast` at `top: var(--gl-space-4xl)` — 56px — and our header ends at
 * 64px. The toast therefore landed on top of it and, being `pointer-events: auto` while
 * visible, swallowed clicks on "Abmelden" for three seconds.
 *
 * The rule itself lives in the element's shadow root and cannot be reached from here, but the
 * custom property can: it inherits across the boundary. So the token is raised for this one
 * subtree instead of overriding a GlassKit rule — which is also the only use of --gl-space-4xl
 * inside glk-toast, so nothing else shifts with it.
 *
 * The safe-area inset has to be added as well: on a Dynamic Island device the header itself
 * moves down by about 59px, and a fixed 78px would put the toast right back on top of it.
 */
#sj-toast {
  --gl-space-4xl: calc(78px + var(--safe-t));
}

/* Narrow, centred column for sign-in and single-column views. */
.sj-narrow {
  max-width: 440px;
  margin: 0 auto;
  padding-top: 6vh;
}

.sj-narrow .muted,
.sj-muted {
  color: var(--gl-color-text-muted);
  font-size: var(--gl-font-size-sm);
  line-height: 1.6;
  margin: 0;
}

.sj-narrow form {
  margin: 0;
}

.sj-narrow code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
}

.sj-center {
  display: flex;
  flex-direction: column;
  gap: var(--gl-space-md);
  align-items: center;
  text-align: center;
  padding-top: 8vh;
}

/* An SPA is unusable without JavaScript — that deserves saying, not hiding. */
html:not(.js) app-root::before {
  content: 'Für dieses Dashboard wird JavaScript benötigt.';
  display: block;
  padding: var(--gl-space-xl);
  text-align: center;
  color: var(--gl-color-text-muted);
}

/* ---- Transitions between views -----------------------------------------------------
 *
 * The swap runs synchronously inside document.startViewTransition() in app.js; here it is
 * only named and timed. Without browser support there is simply a hard cut — pure bonus,
 * not a prerequisite.
 *
 * CAREFUL, learned the expensive way in Voice-Office-Hub: a view-transition-name ISOLATES
 * the element. An element with backdrop-filter must therefore not get one, or the filter
 * has nothing left to filter and the glass effect disappears. The name belongs on the
 * container being swapped, not on the glass surfaces inside it.
 */
#app {
  view-transition-name: sj-content;
}

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(sj-content),
  ::view-transition-new(sj-content) {
    animation-duration: 0.22s;
    animation-timing-function: cubic-bezier(0.22, 0.61, 0.36, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}


/* ---- Betreiberzeile ---------------------------------------------------------------
 *
 * Steht unter jeder Ansicht, auch auf der Anmeldeseite. Leise gesetzt — sie ist kein
 * Bedienelement —, aber vorhanden: Wer einen entgeltlichen Dienst betreibt, muss sich
 * nennen, und eine Anmeldemaske ohne Betreiber ist ausserdem genau das Muster, das
 * Phishing-Erkennungen als Markenmissbrauch lesen.
 *
 * Der Abstand nach unten hält die Zeile über der schwebenden Tab-Leiste frei; ohne ihn
 * verschwände sie dahinter, und zwar nur auf schmalen Geräten.
 */
.sj-legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 10px;
  margin: 40px auto 0;
  padding: 0 var(--safe-l) calc(96px + var(--safe-b)) var(--safe-r);
  max-width: 720px;
  font-size: var(--gl-font-size-xs);
  color: var(--gl-color-text-muted);
  text-align: center;
}
.sj-legal a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.sj-legal a:hover { color: var(--gl-color-primary); }
.sj-legal a:focus-visible {
  outline: 2px solid var(--gl-color-primary);
  outline-offset: 3px;
  border-radius: 3px;
}
