/* ============================================================================
   FindOffCampus — base layer
   Reset, app shell, and the platform behaviours that separate "native app"
   from "website in a phone frame": safe areas, momentum scroll, contained
   overscroll, suppressed tap highlight, 44px targets.

   Flat and 2D throughout. Elevation is a hairline plus a surface step.
   ============================================================================ */

/* ---------------------------------------------------------------- reset -- */

/* `hidden` must actually hide.

   The browser default is [hidden] { display: none }, but any explicit display
   in our own CSS outranks it — and nearly every component here sets one. The
   swap form stayed on screen while the swap was still locked for exactly that
   reason: `.sv-card` is display:flex, so the attribute did nothing at all.

   Components used to each carry their own `.thing[hidden]` rule, which works
   right up until somebody adds a component and forgets. One rule instead, and
   `!important` is correct here because hiding is the entire meaning of the
   attribute. */
[hidden] { display: none !important; }

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

* { margin: 0; padding: 0; }

html {
  /* Stop iOS enlarging text when the device is turned sideways. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  background: var(--c-ground);
  color: var(--c-text);
  font-family: var(--font-ui);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-body);

  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  /* Native feel: no blue flash on tap, no long-press callout, no accidental
     text selection while swiping the feed. Selection is re-enabled below for
     the places people genuinely need to copy from. */
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  user-select: none;

  /* The page itself never scrolls or bounces — inner regions do. */
  overscroll-behavior: none;
  overflow: hidden;
  height: 100dvh;
}

::selection { background: var(--c-selection); color: var(--c-text); }

/* Text people legitimately copy. */
input, textarea, [data-selectable] { user-select: text; -webkit-user-select: text; }

img, video, canvas, svg { display: block; max-width: 100%; }

button, input, select, textarea {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  border-radius: 0;
  -webkit-appearance: none;
  appearance: none;
}

button { cursor: pointer; }

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

ul, ol { list-style: none; }

/* Every interactive element clears Apple's 44px minimum. */
button,
a[role="button"],
[data-tap] {
  min-height: var(--tap-min);
  min-width: var(--tap-min);
}

/* Small visual controls keep a 44px hit area without growing visually. */
[data-tap="compact"] {
  min-height: 0;
  min-width: 0;
  position: relative;
}
[data-tap="compact"]::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: var(--tap-min);
  height: var(--tap-min);
  transform: translate(-50%, -50%);
}

/* Keyboard focus stays visible; pointer focus does not draw a ring. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--c-gold);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* ------------------------------------------------------------ app shell -- */

.app {
  position: relative;
  width: 100%;
  height: 100dvh;
  background: var(--c-ground);
  overflow: hidden;

  /* Flex, not grid.
     With `grid-template-rows: auto 1fr` the window bar took the auto row and
     the frame took 1fr — but the window bar is `display: none` below 768px, so
     it left the grid entirely and the FRAME inherited the auto row. The shell
     then sized to its content instead of the viewport, which only showed once
     there were too few listings to fill the screen.
     Flex does not care whether a child is hidden. */
  display: flex;
  flex-direction: column;
}

/* --- header --- */

.app-header {
  position: relative;
  z-index: var(--z-header);
  height: calc(var(--header-h) + var(--safe-top));
  padding: var(--safe-top) var(--s-4) 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);

  background: var(--c-ground);
  border-bottom: var(--border-faint);
}

.wordmark {
  font-size: var(--t-headline);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-heading);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.wordmark em {
  font-style: normal;
  color: var(--c-text-muted);
  font-weight: var(--fw-semibold);
}

.campus-chip {
  font-size: var(--t-caption-2);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-gold);
  border: var(--border-gold);
  border-radius: var(--r-pill);
  padding: 3px var(--s-2);
  line-height: 1.4;
}

/* --- scroll region --- */

.app-main {
  position: relative;
  min-height: 0;          /* lets the grid row actually constrain the child */
  overflow: hidden;

  /* The grid measures its column width against this, via 100cqw. */
  container-type: inline-size;
}

/* --- tab bar --- */

.app-tabbar {
  position: relative;
  z-index: var(--z-tabbar);
  height: calc(var(--tabbar-h) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;

  background: var(--c-ground);
  border-top: var(--border-faint);
}

.tab {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--c-text-faint);
  transition: color var(--dur-fast) var(--ease-out);
}

.tab[aria-selected="true"] { color: var(--c-text); }

.tab:active { opacity: 0.55; }

.tab svg { width: 25px; height: 25px; }

.header-actions { display: flex; align-items: center; gap: var(--s-1); }

.icon-btn {
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  color: var(--c-text-muted);
  border-radius: var(--r-pill);
  transition: color var(--dur-fast) var(--ease-out);
}

.icon-btn:active { opacity: 0.55; }
.icon-btn[aria-expanded="true"] { color: var(--c-gold); }

/* ----------------------------------------------------------- utilities -- */

.u-hidden { display: none !important; }

.u-label {
  font-size: var(--t-caption-2);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-text-muted);
}

.u-num { font-variant-numeric: tabular-nums; }

.u-scroll {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;   /* momentum scroll on older iOS */
  overscroll-behavior: contain;        /* no rubber-banding through to the page */
  scrollbar-width: none;
}
.u-scroll::-webkit-scrollbar { display: none; }

/* Skeleton placeholder — used instead of blank-then-spinner. Flat: a surface
   step and a slow sweep, no glow. */
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--c-raised);
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.045),
    transparent
  );
  animation: skeleton-sweep 1.4s ease-in-out infinite;
}

@keyframes skeleton-sweep {
  to { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none; }
}
