/*
  Matrix DS mobile layer. Mobile first: the base rules are the phone.

  Load after recipes.css. Nothing here changes a v0.1 page until the page opts in:
    - New tokens and the new .mx-shell, .mx-bar, .mx-scroll, .mx-content, .mx-dock, .mx-stage,
      .mx-stage-cluster classes are inert until used.
    - New phone values apply only under data-mx-density="mobile" on the html element.
    - The lite render tier applies only under data-mx-perf="lite" on the html element.
  An absent attribute means the v0.1 values, byte for byte.

  Breakpoints are constants, because CSS custom properties cannot be used inside @media.
  Min-width only. They are mirrored in tokens/breakpoints.json and checked by tests.
    md  48rem   768px   tablet, and the table to card switch
    lg  64rem   1024px  desktop
    xl  90rem   1440px  wide, gate width only, no layout change required
  Legacy queries in consumers (37.5rem, 40rem, 60rem) stay until each repo's next layout PR.

  One height query, for short screens: 599px, that is under 600px tall. It only tightens the
  stage cluster spacing.

  Two layouts, never mixed on one screen. A short screen (home, login, check email, confirm, 404)
  is a .mx-stage with heading, line, and primary as one cluster and no dock. A long scrolling app
  screen is .mx-shell with .mx-bar, .mx-scroll, and a sticky .mx-dock. Layout classes use
  min-height only, never height, and nothing is flex-pinned to the bottom.

  Safe areas: apps use var(--mx-safe-*), never raw env(). The tokens below are the only place
  env() appears.
*/

:root {
  --mx-hit-min: 2.75rem;
  --mx-hit: 3rem;
  --mx-hit-lg: 3.5rem;
  --mx-hit-gap: 0.5rem;
  --mx-safe-t: env(safe-area-inset-top, 0px);
  --mx-safe-r: env(safe-area-inset-right, 0px);
  --mx-safe-b: env(safe-area-inset-bottom, 0px);
  --mx-safe-l: env(safe-area-inset-left, 0px);
  --mx-bar-h: 3.5rem;
  --mx-dock-h: 5rem;
  --mx-content-max: 72rem;
  --mx-dur-press: 80ms;
  --mx-dur-reveal: 360ms;
  --mx-dur-sheet: 410ms;
  --mx-dur-nav: 440ms;
  --mx-ease-press: cubic-bezier(0.16, 1, 0.3, 1);
  --mx-ease-reveal: var(--mx-ease);
  --mx-ease-sheet: cubic-bezier(0.32, 0.72, 0, 1);
  --mx-ease-nav: cubic-bezier(0.25, 1, 0.5, 1);
}

/* Spring curves for engines that support linear(). The values above are the fallback. */
@supports (transition-timing-function: linear(0, 1)) {
  :root {
    --mx-ease-press: linear(0, 0.112, 0.323, 0.531, 0.697, 0.816, 0.895, 0.945, 0.973, 0.989, 0.997, 1, 1);
    --mx-ease-reveal: linear(0, 0.082, 0.255, 0.444, 0.613, 0.748, 0.848, 0.916, 0.961, 0.987, 1.001, 1.007, 1.009, 1.009, 1.007, 1.006, 1);
    --mx-ease-sheet: linear(0, 0.048, 0.157, 0.29, 0.424, 0.547, 0.653, 0.74, 0.81, 0.865, 0.906, 0.937, 0.959, 0.974, 0.985, 0.992, 1);
    --mx-ease-nav: linear(0, 0.065, 0.201, 0.356, 0.502, 0.626, 0.726, 0.804, 0.863, 0.905, 0.936, 0.957, 0.972, 0.982, 0.989, 0.993, 1);
  }
}

/* Density: the phone values. Put the attribute on html so the short aliases follow. */
[data-mx-density="mobile"] {
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  --mx-gutter: 1rem;
  --mx-card-pad: 1rem;
  --gutter: var(--mx-gutter);
  --card-pad: var(--mx-card-pad);
  --fs-caption: 0.875rem;
  --fs-label: 0.875rem;
}

@media (min-width: 48rem) {
  [data-mx-density="mobile"] {
    --mx-gutter: 1.5rem;
    --mx-card-pad: 1.5rem;
  }
}

/* Tap targets. Buttons take the default size, a primary may take the large one. */
:where([data-mx-density="mobile"]) :is(.mx-btn, .mx-btn-ghost) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--mx-hit-min);
  min-height: var(--mx-hit);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  transition: background-color var(--mx-dur-press) var(--mx-ease-press), transform var(--mx-dur-press) var(--mx-ease-press);
}



/* Press feedback is a color change plus a scale. The color survives reduced motion. */
:where([data-mx-density="mobile"]) .mx-btn:active {
  background: var(--mx-btn-bg-hover);
  transform: scale(0.97);
}

:where([data-mx-density="mobile"]) .mx-btn-ghost:active {
  background: var(--mx-bg-row);
  transform: scale(0.97);
}

/* Inputs never go below 16px, so iOS does not zoom on focus, and never below the 44px floor.
   The attribute selector lets this beat a single consumer class. */
[data-mx-density="mobile"] :is(input, select, textarea) {
  font-size: max(1rem, 16px);
}

[data-mx-density="mobile"] :is(input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="hidden"], [type="file"], [type="color"])), select, textarea) {
  min-height: var(--mx-hit-min);
}

/* Taller targets sit after the floor and at (0, 2, 0), so they beat it. */
[data-mx-density="mobile"] .mx-input,
[data-mx-density="mobile"] [data-mx-primary] {
  min-height: var(--mx-hit);
}

[data-mx-density="mobile"] [data-mx-primary="lg"] {
  min-height: var(--mx-hit-lg);
}

/* Layout shell. Fit screens size to the small viewport, backgrounds may use the dynamic one. */
.mx-shell {
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--mx-bg);
  color: var(--mx-fg);
  font-family: var(--font-sans);
}

.mx-shell[data-mx-fit] {
  min-height: 100vh;
  min-height: 100svh;
}

.mx-bar {
  position: sticky;
  top: 0;
  z-index: 10;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--mx-hit-gap);
  min-height: calc(var(--mx-bar-h) + var(--mx-safe-t));
  padding: var(--mx-safe-t) max(var(--mx-gutter), var(--mx-safe-r)) 0 max(var(--mx-gutter), var(--mx-safe-l));
  background: var(--mx-bg-elev);
  border-bottom: 1px solid var(--mx-border);
}

.mx-scroll {
  min-width: 0;
}

.mx-content {
  box-sizing: border-box;
  width: 100%;
  max-width: var(--mx-content-max);
  margin: 0 auto;
  padding-left: max(var(--mx-gutter), var(--mx-safe-l));
  padding-right: max(var(--mx-gutter), var(--mx-safe-r));
}

.mx-dock {
  position: sticky;
  bottom: 0;
  z-index: 10;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--mx-hit-gap);
  min-height: calc(var(--mx-dock-h) + var(--mx-safe-b));
  padding: var(--mx-space-3) max(var(--mx-space-4), var(--mx-safe-r)) calc(var(--mx-space-3) + var(--mx-safe-b)) max(var(--mx-space-4), var(--mx-safe-l));
  background: var(--mx-bg-elev);
  border-top: 1px solid var(--mx-border);
}

/*
  Stage: a short screen. Heading, line, and primary are one cluster, centered in the small
  viewport with a small optical lift. No dock. The cluster is a grid, so nothing stretches it.
  justify-content falls back to plain center where "safe center" is unsupported.
*/
.mx-stage {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  justify-content: safe center;
  min-height: 100vh;
  min-height: 100svh;
  padding: max(var(--mx-gutter), var(--mx-safe-t)) max(var(--mx-gutter), var(--mx-safe-r)) max(var(--mx-gutter), var(--mx-safe-b)) max(var(--mx-gutter), var(--mx-safe-l));
  background: var(--mx-bg);
  color: var(--mx-fg);
  font-family: var(--font-sans);
}

.mx-stage-cluster {
  box-sizing: border-box;
  display: grid;
  row-gap: var(--mx-space-3);
  width: 100%;
  max-width: 28rem;
  margin-block-end: clamp(0px, 8svh, 72px);
}

.mx-stage-cluster > * {
  margin: 0;
}

.mx-stage-cluster > :is(.mx-btn, .mx-btn-ghost) {
  margin-block-start: var(--mx-space-5);
}

@media (max-height: 599px) {
  .mx-stage-cluster {
    margin-block-end: 0;
  }

  .mx-stage-cluster > :is(.mx-btn, .mx-btn-ghost) {
    margin-block-start: var(--mx-space-3);
  }
}

/* A sticky bar or dock must not hide the focused element. The extra 4px keeps its focus ring clear. */
:where(html:has(.mx-bar)) {
  scroll-padding-top: calc(var(--mx-bar-h) + var(--mx-safe-t) + 4px);
}

:where(html:has(.mx-dock)) {
  scroll-padding-bottom: calc(var(--mx-dock-h) + var(--mx-safe-b) + 4px);
}

/*
  Performance tier. full is the default. lite is for Save-Data, low memory, or a user choice.
  lite keeps every color, size, spacing, and target value, so layout and contrast gates are
  identical. It drops blur shadows, backdrop-filter, and every transition except
  background-color at press speed, including on pseudo-elements. The glow becomes a 3px inset
  accent bar.
*/
[data-mx-perf="lite"] {
  --mx-shadow-card: 0 0 0 1px rgba(0, 0, 0, 0.4);
  --mx-shadow-glow: inset 3px 0 0 var(--mx-accent);
}

:where([data-mx-perf="lite"], [data-mx-perf="lite"] *),
:where([data-mx-perf="lite"] *)::before,
:where([data-mx-perf="lite"] *)::after {
  backdrop-filter: none !important;
  transition-property: background-color !important;
  transition-duration: var(--mx-dur-press) !important;
}

/* Reduced motion, in either attribute and either tier. Durations collapse, loops stop, the
   pressed color cue stays. It sits after the lite rule so it wins. */
@media (prefers-reduced-motion: reduce) {
  :where([data-mx-density="mobile"], [data-mx-density="mobile"] *, [data-mx-perf="lite"], [data-mx-perf="lite"] *),
  :where([data-mx-density="mobile"] *)::before,
  :where([data-mx-density="mobile"] *)::after,
  :where([data-mx-perf="lite"] *)::before,
  :where([data-mx-perf="lite"] *)::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  :where([data-mx-density="mobile"]) :is(.mx-btn, .mx-btn-ghost):active {
    transform: none;
  }
}
