/* Theme and color schemes */
:root {
  box-sizing: border-box;

  scrollbar-width: thin;
  scrollbar-color: gray transparent;
}

.app-tooltip {
  position: absolute;
  z-index: 9999;
  padding: 3px 8px;
  border-radius: 6px;
  background: var(--text-color-primary);
  color: var(--background-color);
  font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: var(--text-70);
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}

.app-tooltip--visible {
  opacity: 1;
  transform: translateY(0);
}

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

:root {
  --background-color: #F2F2F2;
  --background-color-secondary: #FFFFFF;

  --popup-background: rgba(230, 230, 230, 0.5);

  /* Text colors */
  --text-color-primary: #1A1A1A;
  --text-color-secondary: #333333;
  --text-color-accent: #2a9e47;

  /* Status colors */
  --color-status-free: #2a9e47;
  --color-status-free-soon: #5a7a10;
  --color-status-partial: #f0b429;
  --color-status-busy: #e53935;

  --accent-gradient: linear-gradient(135deg, #0d7a30, #2ecc5a);
  --busy-gradient: linear-gradient(135deg, #ff5252, #e53935);

  --shadow:
    0 1px 2px rgba(0, 0, 0, 0.06),
    0 4px 12px rgba(0, 0, 0, 0.08);

  /* Radius scale — matches the values already in use across components,
     not an invented step size. Nested/concentric corners (an inner element
     whose radius is derived from its parent's radius minus padding, so the
     curves stay parallel) are intentionally NOT put on this scale — forcing
     a fixed token there breaks the concentric relationship. */
  --radius-xs: 4px;
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-2xl: 32px;
  --radius-pill: 999px;

  /* Type scale — the 12 sizes actually used 5+ times across components,
     not an invented scale. Values used ≤4 times were snapped into the
     nearest step here only when within ~0.03–0.05rem (imperceptible);
     anything farther off (one-off hero sizes, icon-specific px values)
     was left as a literal rather than forced to fit. */
  --text-70: 0.7rem;
  --text-72: 0.72rem;
  --text-75: 0.75rem;
  --text-80: 0.8rem;
  --text-85: 0.85rem;
  --text-90: 0.9rem;
  --text-100: 1rem;
  --text-110: 1.1rem;
  --text-125: 1.25rem;
  --text-150: 1.5rem;
  --text-175: 1.75rem;
  --text-200: 2rem;

  /* Glass material — the tabbar's recipe (bn-tabbar, pre-tokenization) is
     the canonical source for this; every other translucent surface should
     match it, not the other way around. No saturate() — the tabbar never
     had one. */
  --glass-tint: rgba(250, 250, 250, 0.7);
  --glass-outline: rgba(0, 0, 0, 0.25);
  --glass-shadow-color: rgba(0, 0, 0, 0.1);
  /* The specular rim along the top and bottom edges. Plain white here; the
     HDR block below pushes it past white on displays that have the headroom
     (see the --glass-specular override under `dynamic-range: high`). Dark
     mode redefines --glass-highlight wholesale and doesn't use this. */
  --glass-specular: rgb(255 255 255);
  --glass-highlight:
    inset -1px 0 2px 0 rgba(0, 0, 0, 0.25),
    inset 1px 0 2px 0 rgba(0, 0, 0, 0.25),
    inset 0 -1px 2px 0 var(--glass-specular),
    inset 0 1px 2px 0 var(--glass-specular),
    inset 0 -8px 4px 0 rgba(0, 0, 0, 0.02),
    inset 0 8px 4px 0 rgba(0, 0, 0, 0.02);
  --glass-blur-sm: 4px;
  --glass-blur-md: 8px;
  --glass-blur-lg: 18px;

  /* Outside 0.5px gradient stroke, top to bottom (Figma's outside-stroke
     equivalent). Applied per glass surface via a masked ::after ring —
     see .header-button::after below for the shared recipe. */
  --glass-stroke-gradient: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 1) 0%,
    rgba(94, 94, 94, 0.25) 25%,
    rgba(94, 94, 94, 0.8) 50%,
    rgba(94, 94, 94, 0.25) 75%,
    rgba(255, 255, 255, 1) 100%
  );
  --glass-stroke-opacity: 0.8;

  /* Repeated shadow/glow patterns, extracted only where the literal value
     was byte-identical across multiple files — not a general elevation
     scale, just naming duplication that already existed. */
  --bevel: inset 1px 1px 0 rgba(255, 255, 255, 0.3), inset -1px -1px rgba(255, 255, 255, 0.1);
  --glow-accent: 0 2px 8px color-mix(in srgb, var(--text-color-accent) 40%, transparent);
  --glow-info: 0 2px 8px rgba(33, 150, 243, 0.4);
  --shadow-groove: inset 0 1px 3px rgba(0, 0, 0, 0.1);
  --shadow-dim: 0 1px 2px rgba(0, 0, 0, 0.5);

  /* "Now" marker blue — fixed by design, doesn't react to theme (verified:
     no dark-mode override exists anywhere it's used). */
  --color-now: #2196f3;

  /* Darker/more-saturated green used only inside dark-mode blocks for the
     today/now indicators, for better white-text contrast than
     --text-color-accent's dark value gives (per the original comments). */
  --color-accent-dark: #28a745;
  --color-accent-dark-hover: #218838;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background-color: #1E1E1E;
    --background-color-secondary: #2A2A2A;

    --popup-background: rgba(100, 100, 100, 0.75);

    /* Text colors */
    --text-color-primary: #EAEAEA;
    --text-color-secondary: #CCCCCC;
    --text-color-accent: #3ce065;

    /* Status colors */
    --color-status-free: #3ce065;
    --color-status-free-soon: color-mix(in srgb, var(--color-status-free) 70%, var(--color-status-partial));
    --color-status-partial: #f5a623;
    --color-status-busy: #ff5252;

    --accent-gradient: linear-gradient(135deg, #1fa840, #90ffb8);
    --busy-gradient: linear-gradient(135deg, #f44336, #ff867f);

    --shadow:
      0 1px 2px rgba(0, 0, 0, 0.3),
      0 4px 12px rgba(0, 0, 0, 0.6);

    --glass-tint: rgba(21, 21, 21, 0.8);
    --glass-outline: rgba(255, 255, 255, 0.25);
    --glass-shadow-color: rgba(0, 0, 0, 0.1);
    --glass-highlight:
      inset -1px 0 2px 0 rgba(0, 0, 0, 0.75),
      inset 1px 0 2px 0 rgba(0, 0, 0, 0.75),
      inset 0 -1px 2px 0 rgba(255, 255, 255, 0.2),
      inset 0 1px 2px 0 rgba(255, 255, 255, 0.2),
      inset 0 -8px 4px 0 rgba(255, 255, 255, 0.02),
      inset 0 8px 4px 0 rgba(255, 255, 255, 0.02);
    --glass-stroke-gradient: linear-gradient(
      to bottom,
      rgba(223, 223, 223, 0.35) 0%,
      rgba(41, 41, 41, 0.5) 25%,
      rgba(0, 0, 0, 1) 50%,
      rgba(41, 41, 41, 0.5) 75%,
      rgba(223, 223, 223, 0.35) 100%
    );
  }
}

/* HDR specular highlights.

   In light mode the glass rim was already rgb(255 255 255) — the brightest
   colour SDR can express — so against a light background it barely reads as a
   highlight at all. On a display with dynamic range to spare we spend it here:
   components above 100% in display-p3 are extended-range values, and the
   compositor takes them past reference white instead of clamping.

   Gated on `dynamic-range: high` rather than left to clamp on its own, so SDR
   displays keep the exact previous value and the effect is one token to tune.
   Requires an HDR-capable display *and* the browser actually driving it in HDR
   (Chrome/Edge 136+, Safari 26+); everywhere else the media query simply
   doesn't match.

   The @supports is load-bearing, not belt-and-braces: the media query shipped
   before color() did (Chrome 98 vs 111), so a browser in that gap would store
   an unparseable value in --glass-specular. Custom properties aren't validated
   until substitution, so that wouldn't fall back through the cascade — it
   would invalidate the whole box-shadow at computed-value time and drop it to
   `none`, taking the outline and drop shadow with it. */
@media (dynamic-range: high) {
  @supports (color: color(display-p3 1 1 1)) {
    :root {
      --glass-specular: color(display-p3 200% 200% 200%);
    }
  }
}

/* Base Styles */
html,
body {
  touch-action: manipulation;
}

html {
  height: 100%;

  overflow-x: hidden;
  overflow-y: auto;

  background-color: transparent;
}

body {
  min-height: 100%;
  margin: 0;
  padding: 0;

  display: flex;
  flex-direction: column;

  background-color: var(--background-color);
  scroll-behavior: smooth;
  text-align: center;
  font-family: "Nunito", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Remove the tap highlight for mobile browsers */
  -webkit-tap-highlight-color: transparent;

  user-select: none;
}

.noscript-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;

  background: var(--background-color);

  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
}

/* ── Splash Screen ────────────────────────────────────────────── */
html:has(.splash-overlay) {
  overflow: hidden;
}

.splash-overlay {
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: flex;
  align-items: center;
  justify-content: center;
}

.splash-overlay::before {
  content: '';
  position: absolute;
  inset: 0;
  background-color: var(--background-color);
  transition: opacity 0.65s ease-out;
  pointer-events: none;
}

.splash-overlay.splash-hiding::before {
  opacity: 0;
}

.splash-logo {
  width: 10rem;
  height: 10rem;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  will-change: transform;
  opacity: 0;
  scale: 0.75;
  animation: splash-logo-fade-in 0.5s ease-out 0.1s forwards;
}

@keyframes splash-logo-fade-in {
  to {
    opacity: 1;
    scale: 1;
  }
}

.splash-logo.splash-logo-flying {
  transition: transform 1s cubic-bezier(0.16, 1, 0.3, 1);
}

.splash-header-item {
  opacity: 0;
  transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.splash-header-item.splash-revealed {
  opacity: 1;
}

.splash-loading-label {
  position: absolute;
  bottom: 3.5rem;
  font-size: var(--text-75);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-color-secondary);
  opacity: 0;
  scale: 0.88;
  animation: splash-loading-appear 1s ease-out 3s forwards;
}

.splash-loading-label::after {
  content: '...';
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  width: 0;
  animation: splash-loading-dots 1.4s steps(4, end) 3s infinite;
}

@keyframes splash-loading-appear {
  to {
    opacity: 0.5;
    scale: 1;
  }
}

@keyframes splash-loading-dots {
  to { width: 1.1em; }
}

@media (prefers-reduced-motion: reduce) {
  .splash-logo {
    animation: none;
    opacity: 1;
  }

  .splash-logo.splash-logo-flying {
    transition: transform 0s;
  }

  .splash-loading-label,
  .splash-loading-label::after {
    animation: none;
  }
}

/* Splash error state */
.splash-overlay.splash-error {
  flex-direction: column;
  gap: 0.5rem;
  padding: 2rem;
  text-align: center;
}

.splash-error-icon {
  font-size: 2.5rem;
  color: var(--text-color-secondary);
  margin-bottom: 0.25rem;
  opacity: 0;
  animation: splash-logo-fade-in 0.4s ease-out 0.1s forwards;
}

.splash-error-title {
  font-size: var(--text-110);
  font-weight: 700;
  color: var(--text-color-primary);
  opacity: 0;
  animation: splash-logo-fade-in 0.4s ease-out 0.15s forwards;
}

.splash-error-subtitle {
  font-size: var(--text-90);
  color: var(--text-color-secondary);
  margin-bottom: 1rem;
  opacity: 0;
  animation: splash-logo-fade-in 0.4s ease-out 0.2s forwards;
}

.splash-error-reload {
  opacity: 0;
  animation: splash-logo-fade-in 0.4s ease-out 0.25s forwards;
}

/* Splash logo View Transition */
@supports (view-transition-name: none) {
  /* Pure positional morph — suppress the default cross-fade so it reads as
     one image moving rather than two images dissolving into each other. */
  ::view-transition-old(splash-icon),
  ::view-transition-new(splash-icon) {
    animation: none;
  }

  ::view-transition-group(splash-icon) {
    animation-duration: 0.6s;
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  }
}

/* Generic HTML Elements */
h1 {
  font-size: 3rem;
  font-weight: bold;
  color: var(--text-color-primary);
}

h2 {
  font-size: 2.25rem;
  font-weight: bold;
  color: var(--text-color-primary);
}

h3 {
  font-size: var(--text-175);
  font-weight: 600;
  color: var(--text-color-primary);
}

h4 {
  font-size: var(--text-150);
  font-weight: 500;
  color: var(--text-color-primary);
}

p {
  font-size: var(--text-100);
  color: var(--text-color-primary);
}

a {
  color: var(--text-color-accent);
}

.body-text {
  font-size: var(--text-100);
  color: var(--text-color-primary);
}

.body-small-text {
  font-size: var(--text-85);
  color: var(--text-color-primary);
}

.caption-text {
  font-size: var(--text-75);
  color: var(--text-color-primary);
}

label {
  font-size: var(--text-75);
  font-weight: medium;
  color: var(--text-color-primary);
}

.quote-text {
  font-size: var(--text-100);
  font-style: italic;
  color: var(--text-color-primary);
}

.button-text {
  font-size: var(--text-80);
  font-weight: medium;
  color: var(--text-color-primary);
}

.transparent-button {
  background-color: transparent;
  border-color: transparent;

  cursor: pointer;
}

.secondary {
  color: color-mix(in srgb, var(--text-color-primary) 70%, transparent);
}

.info-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;

  padding: 1rem 1.25rem;
  border-radius: 12px;

  background: color-mix(in srgb, #f5a623 15%, transparent);
  border: 1px solid color-mix(in srgb, #f5a623 30%, transparent);

  mix-blend-mode: multiply;

  width: fit-content;
  align-self: center;
  margin: 0 auto;
}

@media (prefers-color-scheme: dark) {
  .info-box {
    mix-blend-mode: screen;
  }
}

.info-box-icon {
  margin: 0;
  font-size: var(--text-150);
}

.info-box-title {
  margin: 0;
  color: #d4820a;
  font-weight: bold;
}

@media (prefers-color-scheme: dark) {
  .info-box-title {
    color: #f5a623;
  }
}

.info-box-content {
  margin: 0;
  font-size: var(--text-90);
  color: color-mix(in srgb, #d4820a 80%, var(--text-color-primary));
  font-weight: 600;
}

/* --- Styles --- */

/* Header */
.header-top-strip {
  position: sticky;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1001;

  height: 11px;

  background-color: var(--background-color);
}

.header {
  padding-top: calc(env(safe-area-inset-top));

  display: flex;
  flex-direction: column;
  gap: 0;

  position: sticky;
  top: 11px;
  left: 0;
  right: 0;
  z-index: 1000;

  user-select: none;

  isolation: isolate;
}

.header-blur-layers {
  position: absolute;
  inset: 0;
  bottom: -2rem;

  pointer-events: none;
  /* Negative so it paints behind header-top-line-container's own (non-
     positioned) content without needing z-index on every sibling — scoped
     to that container's stacking context via its position+z-index below. */
  z-index: -1;
}

.header-blur-layer {
  position: absolute;
  inset: 0;
}

.header-blur-layer:nth-child(1) {
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  mask-image: linear-gradient(to bottom, transparent 0%, black 12.5%, black 25%, transparent 37.5%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 12.5%, black 25%, transparent 37.5%);
}

.header-blur-layer:nth-child(2) {
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  mask-image: linear-gradient(to bottom, transparent 12.5%, black 25%, black 37.5%, transparent 50%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 12.5%, black 25%, black 37.5%, transparent 50%);
}

.header-blur-layer:nth-child(3) {
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  mask-image: linear-gradient(to bottom, transparent 25%, black 37.5%, black 50%, transparent 62.5%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 25%, black 37.5%, black 50%, transparent 62.5%);
}

.header-blur-layer:nth-child(4) {
  backdrop-filter: blur(1px);
  -webkit-backdrop-filter: blur(1px);
  mask-image: linear-gradient(to bottom, transparent 37.5%, black 50%, black 100%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 37.5%, black 50%, black 100%);
}

.header-fade-overlay {
  position: absolute;
  inset: 0;

  background: linear-gradient(to bottom,
    var(--background-color) 0%,
    color-mix(in srgb, var(--background-color) 70%, transparent) 45%,
    color-mix(in srgb, var(--background-color) 25%, transparent) 70%,
    transparent 100%);
}

.header-top-line-container {
  position: relative;
  z-index: 1;

  padding: 0 1rem;

  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;

  line-height: 2rem;
}

.header-title-container {
  display: flex;
  flex-direction: row;

  align-items: center;
  justify-content: flex-start;
  gap: 0.5rem;
}

.header-title-btn {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  border-radius: 0.5rem;
  -webkit-tap-highlight-color: transparent;
  font: inherit;
}

.header-title-beta-container>h4 {
  margin: 0;
  padding: 0;

  line-height: 0;
}

.header-title-beta-container {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  justify-content: center;
  gap: 0.2em;
}

.header-title {
  font-size: var(--text-200);
  font-weight: bold;

  background: var(--text-color-primary);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;

  mix-blend-mode: multiply;
}

@media (prefers-color-scheme: dark) {
  .header-title {
    mix-blend-mode: normal;
  }
}

.header-logo {
  height: 2rem;

  user-select: none;
  -webkit-user-drag: none;
}

.header-buttons-container {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

.header-button {
  position: relative;

  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-pill);

  /* Same glass material as the tabbar/back button. */
  background: var(--glass-tint);
  backdrop-filter: blur(var(--glass-blur-sm));
  -webkit-backdrop-filter: blur(var(--glass-blur-sm));

  color: var(--text-color-secondary);

  display: flex;
  align-items: center;
  justify-content: center;

  border-color: transparent;

  box-shadow:
    0 0 0 0.1px var(--glass-outline),
    var(--glass-highlight),
    0 4px 40px 0 var(--glass-shadow-color);

  cursor: pointer;

  transition:
    background-color 0.2s ease,
    box-shadow 0.2s ease,
    transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);

  &:hover {
    background: color-mix(in srgb, var(--text-color-primary) 12%, var(--glass-tint));
    transform: scale(1.1);
  }
}

/* Outside 0.5px gradient stroke (Figma's "outside stroke") without
   affecting layout. header-button is a true circle (fixed equal
   width/height), so the ring is masked with a radial-gradient rather than
   the padding + mask-composite: exclude trick used on the pill-shaped
   surfaces (.bn-tabbar::after, .detail-back-btn::after) — that box-corner
   technique has a seam at the four cardinal points of a perfect circle
   (border-radius: 999px on a square box), rendered as a bright spot/arc
   instead of a clean ring. A radial-gradient mask is exact Euclidean
   distance from the center, so it doesn't hit that seam. .bn-search-btn is
   also a true circle and uses the same fix (see bottom-nav.css). */
.header-button::after {
  content: "";
  position: absolute;
  inset: -0.5px;
  border-radius: inherit;

  background: var(--glass-stroke-gradient);
  opacity: var(--glass-stroke-opacity);

  -webkit-mask-image: radial-gradient(circle closest-side, transparent calc(100% - 0.5px), #fff calc(100% - 0.5px));
  mask-image: radial-gradient(circle closest-side, transparent calc(100% - 0.5px), #fff calc(100% - 0.5px));

  pointer-events: none;
}

.pulse-indicator {
  --pulse-color: var(--text-color-secondary);
}

.pulse-indicator.green {
  --pulse-color: var(--text-color-accent);
}

.pulse-indicator.yellow {
  --pulse-color: #f0b429;
}

.pulse-indicator.red {
  --pulse-color: #e53935;
}

.pulse-indicator {
  position: relative;
  width: 1rem;
  height: 1rem;
}

.pulse-indicator::before,
.pulse-indicator::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
}

.pulse-indicator::before {
  width: 100%;
  height: 100%;
  background: var(--pulse-color);
  animation: pulse-dot 2s ease-out infinite;
}

.pulse-indicator::after {
  width: 100%;
  height: 100%;
  border: 2px solid var(--pulse-color);
  animation: pulse-ring 2s ease-out infinite;
}

@keyframes pulse-ring {
  0% {
    transform: translate(-50%, -50%) scale(1);
    opacity: 0.8;
  }

  70% {
    transform: translate(-50%, -50%) scale(1.8);
    opacity: 0;
  }

  100% {
    transform: translate(-50%, -50%) scale(1.8);
    opacity: 0;
  }
}

@keyframes pulse-dot {
  0% {
    transform: translate(-50%, -50%) scale(1);
  }

  70% {
    transform: translate(-50%, -50%) scale(1.1);
  }

  100% {
    transform: translate(-50%, -50%) scale(1);
  }
}

.data-fetch-popover-container {
  display: flex;
  flex-direction: column;

  gap: 0;
}

.data-reload-btn {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.75rem;
  font-size: var(--text-85);
}

.data-reload-icon {
  font-size: var(--text-100);
  transition: transform 0.3s ease;
}

.data-reload-icon.spinning {
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.header-nav-row {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 1rem;
  /* Collapses to 0 by default so it doesn't eat space in the header when the
     back button isn't shown. The back button is absolutely positioned (out
     of flow), so once it's visible this row has no in-flow content to size
     itself against — without an explicit height it collapses to ~0 and the
     button (vertically centered via its own top:50%/translateY) ends up
     overflowing it top and bottom. 3.5rem gives the button (34px tall)
     comfortable clearance from the header bar above. */
}

body.detail-open .header-nav-row,
body.info-open .header-nav-row {
  min-height: 3.5rem;
}

.material-symbols-outlined {
  font-size: 22px !important;
}

@keyframes slide-in-top {
  from {
    opacity: 0;
    filter: blur(4px);
    transform: translateY(-20px);
    scale: 0.92;
  }

  to {
    opacity: 1;
    filter: blur(0px);
    transform: translateY(0);
    scale: 1;
  }
}

/* Footer */
.footer {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;

  padding: 1rem;
  /* --bottom-nav-height is only the pill bar's own rendered height; the bar
     also floats 20px clear of the viewport edge (see .bn-wrapper's
     `bottom: 20px`), so that offset has to be added back in here. */
  padding-bottom: calc(var(--bottom-nav-height, 88px) + 20px);
}

@media (min-width: 600px) {
  /* Rail moves to the top-left at this width, so the bottom clearance above
     no longer applies (see the matching breakpoint in bottom-nav.css). */
  .footer {
    padding-bottom: 1rem;
  }
}

.version-info-button>label {
  text-decoration: underline;
  font-family: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

.info-label {
  text-align: end;
}

.slide-in-top {
  animation: slide-in-top 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* Tab content */
.body-container {
  padding-top: 0.5rem;
  padding-bottom: var(--bottom-nav-height, 88px);

  flex: 1;
  display: flex;
  flex-direction: column;
}

@media (min-width: 600px) {
  .body-container {
    padding-bottom: 0;
  }
}

.tab-content {
  padding: 0 1rem;

  /* content-visibility:hidden instead of display:none: it still keeps the
     subtree out of layout/paint, but (unlike display:none) doesn't remove it
     from the render tree, so content-visibility:auto on descendants (e.g.
     .classroom-card) keeps working — switching tabs doesn't force a full
     "unhide and lay out everything" pass over the tab being shown. */
  content-visibility: hidden;
}

@media (min-width: 600px) {
  .tab-content {
    /* Clears the fixed side rail: its own left offset (20px) + its measured
       width (--side-nav-width, kept live by bottom-nav.js) + breathing room. */
    padding-left: calc(20px + var(--side-nav-width, 88px) + 1.5rem);
  }
}

.tab-content.visible {
  content-visibility: visible;

  flex: 1;
  display: flex;
  flex-direction: column;

  /* No `forwards`: with it, the frozen final keyframe leaves `filter:
     blur(0)` / `transform: scale(1,1)` permanently applied as live
     properties (not `none`) once the animation ends. Safari treats even a
     no-op filter like that as an active filter root and refuses to
     composite a *nested* backdrop-filter underneath it (e.g. the date
     picker's lifted indicator blurring inside its already-blurred
     container) — Firefox doesn't have this quirk. Nothing else here sets
     filter/transform at rest, so dropping `forwards` reverts them to
     `none` after the 0.3s animation completes, which is visually identical
     but stops blocking descendants' own backdrop-filter in Safari. */
  animation: appear 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes appear {
  from {
    opacity: 0;
    filter: blur(8px);
    transform: scale(0.95, 1);
  }

  to {
    opacity: 1;
    filter: blur(0);
    transform: scale(1, 1);
  }
}

@keyframes disappear {
  from {
    opacity: 1;
    filter: blur(0);
    transform: scale(1);
  }

  to {
    opacity: 0;
    filter: blur(8px);
    transform: scale(0.95);
  }
}

@keyframes i18n-pop {
  from {
    opacity: 0;
    transform: translateY(4px) scale(0.96);
    filter: blur(3px);
  }

  to {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: blur(0);
  }
}

.i18n-animate {
  animation: i18n-pop 0.2s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.section-header {
  display: flex;
  flex-direction: column;

  justify-content: flex-start;
  text-align: start;

  padding: 1rem 0;
}

.section-header-title,
.section-header-subtitle,
.subsection-header-title,
.subsection-header-subtitle {
  margin: 0;
  padding: 0;
}

.section-header-title {
  font-size: var(--text-200);
  font-weight: bold;
}

.section-header-subtitle {
  font-size: 1.35rem;
}

@media (max-width: 600px) {
  .section-header-title {
    font-size: var(--text-175);
  }

  .section-header-subtitle {
    font-size: var(--text-100);
  }
}

.subsection-header-title {
  font-size: var(--text-150);
  font-weight: 600;
}

.subsection-header-subtitle {
  font-size: var(--text-85);
}

@media (max-width: 600px) {
  .subsection-header-title {
    font-size: var(--text-125);
  }

  .subsection-header-subtitle {
    font-size: var(--text-75);
  }
}

.form {
  display: flex;
  flex-direction: column;

  text-align: center;
  justify-content: center;

  gap: 1rem;
}

.available-classroom-results {
  color: var(--text-color-primary);
}

/* ── Skeleton loading states ──────────────────────────────────── */

@keyframes skeleton-shimmer {
  from {
    background-position: 200% center;
  }

  to {
    background-position: -200% center;
  }
}

.date-picker-skeleton,
.time-pickers-skeleton {
  display: none;
}

form[data-loading] .date-picker-skeleton {
  display: flex;
  flex-direction: row;
  align-items: center;
  padding: 0.5rem 0.75rem;
  border-radius: 999px;
  margin-top: 2rem;
  align-self: center;
  background-color: var(--background-color-secondary);
  box-shadow: var(--bevel), var(--shadow);
}

form[data-loading] .date-picker-skeleton>div {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 8px;
  flex-shrink: 0;
}

form[data-loading] .time-pickers-skeleton {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

form[data-loading] .time-pickers-skeleton>div {
  height: 3.5rem;
  border-radius: 1.75rem;
  flex: 1;
}

form[data-loading] .time-pickers-skeleton>div:last-child {
  height: 1.4rem;
  border-radius: 0.7rem;
  opacity: 0.5;
}

form[data-loading] .date-picker-skeleton>div,
form[data-loading] .time-pickers-skeleton>div {
  background: linear-gradient(90deg,
      color-mix(in srgb, var(--text-color-primary) 6%, transparent) 25%,
      color-mix(in srgb, var(--text-color-primary) 12%, transparent) 50%,
      color-mix(in srgb, var(--text-color-primary) 6%, transparent) 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.6s linear infinite;
}

ul li::marker {
  color: var(--text-color-accent);
}

.popover {
  position: absolute;
  z-index: 1000;

  max-width: 35%;
  word-wrap: break-word;

  padding: 12px 16px;

  /* Same glass material as the tabbar/header-button — see the recipe note
     at :root's --glass-tint definition. */
  background: var(--glass-tint);
  backdrop-filter: blur(var(--glass-blur-md));
  -webkit-backdrop-filter: blur(var(--glass-blur-md));
  color: var(--text-color-primary);

  border-radius: 12px;
  box-shadow:
    0 0 0 0.1px var(--glass-outline),
    var(--glass-highlight),
    0 4px 40px 0 var(--glass-shadow-color);

  opacity: 0;
  transform: scale(0.78) translateY(var(--popover-closed-ty, -6px));

  pointer-events: none;

  transition:
    opacity 120ms ease-out,
    transform 200ms cubic-bezier(0.34, 1.3, 0.64, 1);

  will-change: opacity, transform;
}

/* Outside 0.5px gradient stroke — same recipe as .detail-back-btn::after
   in classroom-detail.css. */
.popover::after {
  content: "";
  position: absolute;
  inset: -0.5px;
  border-radius: inherit;
  padding: 0.5px;

  background: var(--glass-stroke-gradient);
  opacity: var(--glass-stroke-opacity);

  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;

  pointer-events: none;
}

@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .popover {
    background: color-mix(in srgb, var(--background-color-secondary) 95%, black);
  }

  @media (prefers-color-scheme: dark) {
    .popover {
      background: color-mix(in srgb, var(--background-color-secondary) 95%, white);
    }
  }
}

@media (max-width: 600px) {
  .popover {
    max-width: 60%;
  }
}

.popover[data-show] {
  opacity: 1;
  transform: scale(1) translateY(0);

  pointer-events: auto;
}

.arrow {
  position: absolute;
  width: 10px;
  height: 10px;
  background: inherit;
  /* Matches the popover's own glass blur so the part of the triangle poking
     out past the box reads as the same material rather than a flat chip of
     color. (A `clip-path: path()` notch cut directly into the popover body
     was tried instead — merging the two into one shape — but Chromium
     doesn't reliably paint backdrop-filter/background into the part of a
     clip-path that extends past the element's own box: a notch pointing
     "after" the box painted solid, one pointing "before" it painted as a
     near-invisible sliver. Not something to build the arrow's visibility on.) */
  backdrop-filter: inherit;
  -webkit-backdrop-filter: inherit;
  transform: rotate(45deg);
}

/* The arrow is a rotated square straddling the popover's edge — half of it
   sits behind the popover box, half pokes out. Left unclipped, that inner
   half paints flat (no highlight/stroke) on top of the glass box's own edge
   decoration, showing as a seam. Clipping to just the outward-pointing
   triangle removes it. popover.js sets data-side to match the box edge the
   arrow is attached to (see `_updateAnchorOriginAndArrow`/`computeFloatingPosition`). */
.arrow[data-side="top"] { clip-path: polygon(0 0, 100% 0, 0 100%); }
.arrow[data-side="bottom"] { clip-path: polygon(100% 0, 100% 100%, 0 100%); }
.arrow[data-side="left"] { clip-path: polygon(0 0, 0 100%, 100% 100%); }
.arrow[data-side="right"] { clip-path: polygon(0 0, 100% 0, 100% 100%); }

@supports (anchor-name: --a) {
  .popover {
    position: fixed;
    inset: auto;
    top: anchor(bottom);
    right: anchor(right);
    margin: 8px 0 0 0;
    position-try-fallbacks: --popover-above, flip-inline, --popover-above flip-inline;
    position-visibility: anchors-visible;
  }

  @position-try --popover-above {
    inset: auto;
    bottom: anchor(top);
    right: anchor(right);
    margin: 0 0 8px 0;
  }
}

.popover-title {
  font-size: 1.65rem;

  margin: 0;
  align-self: center;

  background: var(--accent-gradient);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;

  mix-blend-mode: multiply;
}

@media (prefers-color-scheme: dark) {
  .popover-title {
    mix-blend-mode: normal;
  }
}

.popover-title.green {
  background: linear-gradient(135deg, #0d7a30, #2ecc5a);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.popover-title.yellow {
  background: linear-gradient(135deg, #b07d00, #f0b429);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.popover-title.red {
  background: linear-gradient(135deg, #a01010, #e53935);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.popover-subtitle {
  font-size: var(--text-125);

  margin: 0 0 0.5rem 0;
}

.changelog-logo {
  height: 4rem;
  margin-bottom: 0.5rem;

  user-select: none;
  -webkit-user-drag: none;
}

.changelog-container {
  text-align: start;

  display: flex;
  flex-direction: column;
}

.popover ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;

  text-align: start;
}

.popover ul li {
  font-size: var(--text-90);
  color: var(--text-color-secondary);
  padding-left: 16px;
  position: relative;
}

.popover ul li::before {
  content: '';
  position: absolute;
  left: 4px;
  top: 0.45em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--text-color-secondary);
}

.hero-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  align-self: center;

  padding: 0.6rem 1rem;
  margin: 0.5rem;

  background-color: rgba(30, 30, 30, 0.08);
  border-radius: 9999px;
  text-decoration: none;
  font-weight: 600;
  color: #24292f;

  box-shadow: var(--shadow);

  transition: transform 0.2s ease, background-color 0.2s ease;
}

.hero-badge:hover {
  transform: scale(1.1);
  background-color: rgba(30, 30, 30, 0.15);
}

.hero-icon {
  width: 1.4rem;
  height: 1.4rem;
  color: #24292f;
}

.hero-text {
  font-size: var(--text-90);
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
  .hero-badge {
    background-color: rgba(255, 255, 255, 0.08);
    color: #fff;
  }

  .hero-badge:hover {
    background-color: rgba(255, 255, 255, 0.15);
  }

  .hero-icon {
    color: #fff;
  }
}

.button-primary {
  display: flex;
  flex-direction: row;
  gap: 0.25rem;

  align-items: center;
  justify-content: center;

  font-size: var(--text-100);
  font-weight: bold;
  font-family: "Nunito", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  width: fit-content;
  align-self: center;

  padding: 0.5rem 1rem;

  border-radius: 999px;

  background-color: color-mix(in srgb, var(--text-color-accent) 28%, var(--background-color-secondary));
  border: 1.5px solid color-mix(in srgb, var(--text-color-accent) 45%, transparent);
  color: var(--text-color-accent);

  box-shadow:
    inset 1px 2px 0 rgba(255, 255, 255, 0.4),
    inset -1px -1px 0 rgba(255, 255, 255, 0.15),
    var(--shadow);

  transition:
    scale 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
    box-shadow 0.2s ease,
    background-color 0.2s ease;

  -webkit-tap-highlight-color: transparent;
  user-select: none;
  cursor: pointer;
}

@media (hover: hover) {
  .button-primary:hover {
    scale: 1.05;
    background-color: color-mix(in srgb, var(--text-color-accent) 38%, var(--background-color-secondary));
    box-shadow:
      inset 1px 2px 0 rgba(255, 255, 255, 0.4),
      inset -1px -1px 0 rgba(255, 255, 255, 0.15),
      0 4px 20px color-mix(in srgb, var(--text-color-accent) 35%, transparent),
      var(--shadow);
  }
}

.button-primary:active {
  scale: 0.96;
  transition:
    scale 0.12s cubic-bezier(0.34, 1.56, 0.64, 1),
    box-shadow 0.12s ease,
    background-color 0.12s ease;
}

.button-primary:not(:active) {
  transition:
    scale 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
    box-shadow 0.3s ease,
    background-color 0.3s ease;
}

.button-primary.button-secondary {
  background: color-mix(in srgb, var(--text-color-accent) 12%, transparent);
  border-color: color-mix(in srgb, var(--text-color-accent) 25%, transparent);
  box-shadow: none;
}

@media (hover: hover) {
  .button-primary.button-secondary:hover {
    background: color-mix(in srgb, var(--text-color-accent) 20%, transparent);
    box-shadow: none;
  }
}

.button-secondary:active {
  scale: 0.93;
}

/* 
Variants for Samsung Internet,
since it doesn't support many features
in its compositor.
All fancy elements like blurs or blend modes
are rendered as solid colors
*/
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .no-safari .bn-tabbar,
  .no-safari .bn-search-btn {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: var(--background-color-secondary);
  }

  @media (prefers-color-scheme: dark) {
    .no-safari .bn-tabbar,
    .no-safari .bn-search-btn {
      background: rgba(55, 55, 55, 0.96);
    }
  }
}

.samsung .bn-tabbar,
.samsung .bn-search-btn,
.samsung .header-button {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: var(--background-color-secondary);
}

.samsung .header-title,
.samsung .popover-title {
  mix-blend-mode: normal;
  -webkit-text-fill-color: unset;
  /* fallback to plain color */
  background: none;
  color: var(--text-color-accent);
}

.samsung .info-box {
  mix-blend-mode: normal;
}

.samsung .arrow {
  display: none;
  /* background: inherit is broken, just hide it */
}

/* ── Desktop two-column layout (Available screen) ── */
@media (min-width: 1100px) {
  .section-header-title {
    font-size: var(--text-175);
  }

  .section-header-subtitle {
    font-size: var(--text-110);
  }

  .subsection-header-title {
    font-size: var(--text-125);
  }

  .building-name {
    font-size: var(--text-150);
  }

  .classroom-name,
  .classroom-status-txt {
    font-size: var(--text-125);
  }

  #available-classrooms-container.visible {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto 1fr;
    column-gap: 1.5rem;
    /* Don't reset padding-left here — it's carrying the side rail's
       clearance from the .tab-content rule at the 600px breakpoint. */
    padding-right: 2rem;
  }

  #available-classrooms-container>.section-header {
    grid-column: 1;
    grid-row: 1;
  }

  #available-classrooms-form {
    grid-column: 1;
    grid-row: 2;
    align-self: start;
  }

  #available-classrooms-results {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: start;

    position: sticky;
    top: calc(var(--header-height, 80px) + 1rem);

    max-height: calc(100dvh - var(--header-height, 80px) - 2rem);
    overflow-y: auto;

    margin: 1rem 0;
  }

  #available-classrooms-results.empty {
    position: static;
    align-self: stretch;
    max-height: none;
  }
}

/* ── Global reduced-motion fallback ──────────────────────────────────────── */
/* Keeps opacity-only fades; removes spatial motion and decorative loops.    */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}