/* Project-wide styles.
 *
 * Tabler owns the component vocabulary: `.card`, `.btn`, `.alert`,
 * `.container` and everything else it defines are never redefined here. An
 * earlier version of this file did redefine them, and the two sets of rules
 * fought each other on every page.
 *
 * This file holds two things: the TD-08 palette, expressed as `--tblr-*`
 * overrides, and the components Tabler has no equivalent for — the fixed top
 * bar, the client's bottom navigation, the login shell, the photo list, the
 * status badge and the toast.
 *
 * Our own components carry an `app-` prefix. It is not decoration: a rule
 * of ours that wears a Tabler class name silently becomes a redefinition of
 * that component, which is how the toast ended up invisible — Tabler hides
 * its own toast unless it carries `.show`.
 */

/* --- TD-08 palette --------------------------------------------------------
 *
 * Fixed, validated values from the Frontend Foundation decision. They are
 * declared on Tabler's own token names so Tabler's components — buttons,
 * badges, links, alerts — pick them up without a rule here.
 *
 * The palette is measured, not eyeballed: the validator
 * (`scripts/validate_palette.js` of the data-visualisation skill) was run
 * against both surfaces. On the dark one the three identity hues keep the
 * same separation they have on the light one (CVD ΔE 8.6, normal-vision
 * 29.7), so the badges are unchanged; only the colours that *are* text —
 * the link and the error text — get a lighter shade, because a colour
 * that carries a white label on it and a colour that is the label cannot
 * be the same colour on a dark surface.
 */

:root {
  /* The identity colours, ours in both themes. They carry a white label
   * on them, and that pairing was measured on the light surface: the
   * badge reads 5.17 against the blue, 5.02 against the green and 4.83
   * against the red, and it does not change with the theme around it. */
  color-scheme: light;

  /* Identity — "en progreso". */
  --tblr-primary: #2563eb;
  --tblr-primary-rgb: 37, 99, 235;
  --tblr-blue: #2563eb;
  --tblr-blue-rgb: 37, 99, 235;

  /* "Terminado". */
  --tblr-green: #15803d;
  --tblr-green-rgb: 21, 128, 61;

  /* "Atrasado". */
  --tblr-red: #dc2626;
  --tblr-red-rgb: 220, 38, 38;

  /* The four milestone statuses. "Pendiente" is a de-emphasis fill, never a
   * categorical slot: it always ships with an icon and a text label (TD-08,
   * rule 1), and it carries dark ink because #94a3b8 is too light to hold
   * white text at 3:1.
   */
  --tblr-status-progress: #2563eb;
  --tblr-status-progress-fg: #ffffff;
  --tblr-status-done: #15803d;
  --tblr-status-done-fg: #ffffff;
  --tblr-status-late: #dc2626;
  --tblr-status-late-fg: #ffffff;
  --tblr-status-pending: #94a3b8;
  --tblr-status-pending-fg: #1f2937;

  /* The card surface. Tabler declares it on `.card` alone, never on the
   * root, so our own surfaces — the top bar, the bottom navigation, the
   * login shell — would have no background at all without this. */
  --tblr-card-bg: var(--tblr-bg-surface);

  /* The error text. Tabler's danger is a *fill*: it carries a white
   * label on it. As text on the dark surface it measures 3.25, under the
   * 4.5 floor, so the dark theme gives the text its own lighter shade. */
  --app-error-text: var(--tblr-danger);

  /* Layout constants. */
  --app-topbar-h: 56px;
  --app-bottombar-h: 64px;
}

/* Our light surfaces. Tabler's own dark theme takes over in the dark one:
   every surface that floats above the page — the inputs, the menus, the
   modals — is already the right colour there, and a theme of warm cards
   with cold grey menus would read as a patchwork. */
:root:not([data-bs-theme='dark']) {
  --tblr-body-bg: #faf8f4; /* page */
  --tblr-body-color: #262626; /* text */
  --tblr-border-color: #e2e8f0; /* track, grid, chrome */

  --tblr-link-color: #2563eb;
  --tblr-link-color-rgb: 37, 99, 235;
}

/* The dark theme. The link has to be readable on the dark surface: ours
 * measures 2.93 there, under the 4.5 floor for text, and this one measures
 * 5.95 on the card and 7.05 on the page. */
:root[data-bs-theme='dark'] {
  color-scheme: dark;

  --tblr-link-color: #60a5fa;
  --tblr-link-color-rgb: 96, 165, 250;

  --app-error-text: #f87171;
}

/* The danger colour is a *fill* on the light surface: the button carries
 * a white label on it and the alert reads it as text. On the dark surface
 * that text measures 3.01 — the least readable thing on the page — so
 * the dark theme gives the danger a lighter shade and the button a dark
 * label: 4.90 as text, 6.57 as a label on the fill. */
:root[data-bs-theme='dark'] {
  --tblr-danger: #f87171;
  --tblr-danger-fg: #171717;
  --tblr-danger-rgb: 248, 113, 113;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

body {
  font-family: var(--tblr-font-sans-serif);
  color: var(--tblr-body-color);
  background: var(--tblr-body-bg);
  min-height: 100vh;
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* Only the two portal layouts have a fixed top bar.
 *
 * The bar grows by the top safe-area inset on a phone with a notch, and
 * the body's padding grows with it: the brand must not sit under the
 * status bar, and the content must not sit under the bar.
 */
body.has-top-bar {
  padding-top: calc(var(--app-topbar-h) + env(safe-area-inset-top, 0px));
}

/* --- Fixed top bar -------------------------------------------------------- */

.top-bar {
  position: fixed;
  inset: 0 0 auto 0;
  height: calc(var(--app-topbar-h) + env(safe-area-inset-top, 0px));
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: env(safe-area-inset-top, 0px) 16px 0;
  background: var(--tblr-card-bg);
  border-bottom: 1px solid var(--tblr-border-color);
  z-index: 10;
}
/* The brand is the longest thing in the bar, so it is the thing that
 * gives way: it truncates rather than pushing the sign-out off the
 * screen of a 360 px phone.
 */
.top-bar .brand {
  font-weight: 700;
  color: var(--tblr-body-color);
  text-decoration: none;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.top-bar .user { color: var(--tblr-secondary); font-size: 14px; }
.top-bar-nav { display: flex; align-items: center; gap: 8px; flex: none; }

/* A form inside the bar: the sign-out is a POST, not a link. */
.top-bar-form { display: flex; margin: 0; }

/* --- Client bottom navigation --------------------------------------------- */

.bottom-nav {
  position: fixed;
  inset: auto 0 0 0;
  height: var(--app-bottombar-h);
  display: flex;
  background: var(--tblr-card-bg);
  border-top: 1px solid var(--tblr-border-color);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  z-index: 10;
}
.bottom-nav .nav-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  color: var(--tblr-secondary);
  text-decoration: none;
  font-size: 12px;
  min-height: 48px;
}
.bottom-nav .nav-item[aria-current="page"] { color: var(--tblr-primary); }
.bottom-nav .icon { width: 24px; height: 24px; }

/* The sign-out is a POST, so it is a form — and it looks like the rest. */
.bottom-nav-form { display: flex; margin: 0; }
.bottom-nav-form .nav-item {
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

/* Keeps the last row of content clear of the fixed bottom navigation. */
.has-bottom-nav {
  padding-bottom: calc(var(--app-bottombar-h) + env(safe-area-inset-bottom, 0px));
}

/* --- The client's home ---------------------------------------------------- */

/* The delivery date is the one figure the client opens the app to read,
 * so it is drawn larger than a heading: 48 px, not a title's size.
 */
.app-hero {
  font-size: 3rem;
  line-height: 1.1;
  font-weight: 700;
  margin: 0;
}

/* The «add to the home screen» banner. It is in the flow of the page,
 * not fixed over it: the content is never covered (US-09-12).
 */
.app-install-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  margin-bottom: 16px;
  background: var(--tblr-card-bg);
  border: 1px solid var(--tblr-border-color);
  border-radius: var(--tblr-border-radius);
}
.app-install-banner .app-install-text { flex: 1; font-size: 14px; }
.app-install-banner .btn { min-height: 44px; }

/* --- Login shell ---------------------------------------------------------- */

.login-shell {
  max-width: 360px;
  margin: 64px auto;
  padding: 24px;
  background: var(--tblr-card-bg);
  border: 1px solid var(--tblr-border-color);
  border-radius: var(--tblr-border-radius);
}
.login-shell h1 { margin: 0 0 4px; }
.login-shell .lead { color: var(--tblr-secondary); margin: 0 0 16px; }
.login-form { display: flex; flex-direction: column; gap: 12px; }
.login-form label { display: flex; flex-direction: column; gap: 4px; font-size: 14px; }
.login-form input {
  padding: 10px 12px;
  border-radius: var(--tblr-border-radius);
  border: 1px solid var(--tblr-border-color);
  background: var(--tblr-card-bg);
  color: var(--tblr-body-color);
  font: inherit;
  min-height: 44px;
}

/* --- Uploads and photos ---------------------------------------------------- */

/* The client's gallery: the photos of a stage, newest first. */
.app-photo-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.app-photo-grid img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--tblr-border-radius);
}

/* RNF-01: the primary actions of the daily screen are at least 44 px
 * tall, so they can be hit with a thumb on site. */
.app-touch-target { min-height: 44px; }

.upload-form { display: flex; flex-direction: column; gap: 12px; }
.upload-form label { display: flex; flex-direction: column; gap: 4px; font-size: 14px; }
.upload-form input {
  padding: 8px 10px;
  border-radius: var(--tblr-border-radius);
  border: 1px solid var(--tblr-border-color);
  background: var(--tblr-card-bg);
  color: var(--tblr-body-color);
  font: inherit;
}

.photo-list { display: grid; gap: 12px; margin-top: 16px; }

.photo-row {
  display: flex;
  gap: 12px;
  background: var(--tblr-card-bg);
  border: 1px solid var(--tblr-border-color);
  border-radius: var(--tblr-border-radius);
  padding: 12px;
  align-items: center;
}
.photo-row img { width: 88px; height: 88px; object-fit: cover; border-radius: var(--tblr-border-radius); }
.photo-row .meta { display: flex; flex-direction: column; gap: 4px; }

/* --- Toasts ----------------------------------------------------------------
 *
 * Named `.app-toast`, not `.toast`. Tabler ships a toast component of its
 * own and hides it unless it carries `.show` — a toast of ours wearing that
 * name would never appear on the screen at all.
 */

.app-toast-container {
  position: fixed;
  right: 16px;
  bottom: calc(var(--app-bottombar-h) + 16px);
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 1000;
}
.app-toast {
  display: block;
  /* A very dark box on the light page, a very light one on the dark page:
     the dark theme's page is #171717, and a toast the same colour would
     vanish into it. */
  background: light-dark(var(--tblr-gray-900), var(--tblr-gray-200));
  color: light-dark(#ffffff, var(--tblr-gray-900));
  padding: 10px 14px;
  border-radius: var(--tblr-border-radius);
  font-size: 14px;
  max-width: 320px;
}
/* The error and the success keep a white label on them: the fill is the
   same in both themes, and so is the contrast of that label. */
.app-toast.app-toast-error {
  background: var(--tblr-red);
  color: #ffffff;
}
.app-toast.app-toast-success {
  background: var(--tblr-green);
  color: #ffffff;
}

/* --- The error text -------------------------------------------------------
 *
 * Tabler's danger is a *fill*: it carries a white label on it. As text on
 * the dark surface it measures 3.25, under the 4.5 floor, so the text gets
 * its own shade — the fill keeps its white label, and the text gets one
 * that reads on the surface it is on.
 */

.app-error-text {
  color: var(--app-error-text);
}

/* --- Status badges --------------------------------------------------------
 *
 * TD-08's rule: a status never travels on colour alone. Every badge carries
 * an icon and a text label, so the colour is a reinforcement and never the
 * message.
 */

.app-status {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.app-status-pending { background-color: var(--tblr-status-pending); color: var(--tblr-status-pending-fg); }
.app-status-progress { background-color: var(--tblr-status-progress); color: var(--tblr-status-progress-fg); }
.app-status-done { background-color: var(--tblr-status-done); color: var(--tblr-status-done-fg); }
.app-status-late { background-color: var(--tblr-status-late); color: var(--tblr-status-late-fg); }

/* --- htmx loading indicator -----------------------------------------------
 *
 * htmx adds `htmx-request` to the element named by `hx-indicator` while the
 * request is in flight. Tabler ships no rule for it, so the reveal lives
 * here. The ring itself is Tabler's `.spinner-border`; this only controls
 * whether it is shown. The element keeps its slot in the layout when idle,
 * so nothing shifts when a request starts.
 */

.htmx-indicator {
  opacity: 0;
  visibility: hidden;
  transition: opacity 150ms ease-in;
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
  visibility: visible;
}

/* --- Ping region ---------------------------------------------------------- */

.ping-region { margin-top: 12px; }
