/*
 * The web layer's stylesheet: a hand port of the V4 redesign's globals.css and
 * of the style values its screens set inline, from kirlts/traza-ambiental-mvp
 * at commit e883297.
 *
 * Why hand-written CSS and not Tailwind. The V4 screens were written with
 * Tailwind v4 in a Next.js build, but almost everything they draw comes from
 * `style={{...}}` literals, not from utility classes: the login screen uses
 * Tailwind for hover and focus states and little else. Porting those literals
 * into named classes here reproduces the design exactly without adding a
 * Node toolchain to a repository that has none, and without the Tailwind Play
 * CDN, which compiles styles in the visitor's browser at runtime and is not
 * meant for production. The cost is that a new screen ported from V4 has its
 * inline values turned into classes by hand, in this file.
 *
 * What was deliberately left behind from globals.css:
 *   - the shadcn/ui token set (chart, sidebar, popover, the oklch greys) and
 *     the `.dark` theme: no ported screen uses them yet, and V4 itself shipped
 *     its dark mode commented out;
 *   - the demo-mode animations, which belong to a screen not ported, and the
 *     landing page's two: the typing effect cycled through the six priority
 *     products the knowledge base rules out (templates/landing.html says
 *     why), and the floating logo is motion with nothing to say, kept as a
 *     still glow instead;
 *   - the `!important` war on input colours. V4 needed it to override
 *     Tailwind's preflight; here nothing competes, so plain rules suffice.
 *
 * Fonts: V4 loads Geist through next/font, which self-hosts it at build time.
 * Nothing loads it here, so the stack falls through to Arial, which is V4's
 * own declared fallback. Loading it from Google Fonts was rejected because
 * every page view would hand the visitor's IP address to a third party, a
 * data-protection exposure this project has not decided to take on.
 * Self-hosting the font files is the open alternative.
 */

:root {
  /* V4's emerald brand scale, as its screens use it inline. */
  --brand-900: #064e3b;
  --brand-700: #047857;
  --brand-600: #059669;
  --brand-500: #10b981;
  --brand-200: #a7f3d0;
  --brand-100: #d1fae5;
  --brand-50: #ecfdf5;

  --text-strong: #111827;
  --text: #374151;
  --text-muted: #4b5563;
  --text-subtle: #6b7280;
  --placeholder: #9ca3af;

  --surface: #ffffff;
  --surface-muted: #f9fafb;
  --border: #e5e7eb;

  --error-bg: #fef2f2;
  --error-text: #991b1b;

  --radius: 0.625rem;
  --font-sans: "Geist", Arial, Helvetica, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

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

body {
  margin: 0;
  font-family: var(--font-sans);
  line-height: 1.6;
  color: var(--text-strong);
  background: var(--surface-muted);
  -webkit-font-smoothing: antialiased;
}

/* Form inputs: V4's globals.css guarantees dark, legible input text on every
   input, including browser autofill, which otherwise paints its own colours. */
input,
textarea,
select {
  font: inherit;
  color: #0f172a;
}

input::placeholder,
textarea::placeholder {
  color: var(--placeholder);
  opacity: 1;
}

input:-webkit-autofill {
  -webkit-text-fill-color: #0f172a;
  -webkit-box-shadow: 0 0 0 1000px var(--surface-muted) inset;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   App shell: the header of V4's role sidebars (logo, wordmark, 64px band with
   an emerald hairline), laid out as a top bar. V4's sidebars are fixed 256px
   columns whose content area has a 944px minimum width, which does not fit a
   phone; a top bar does, and the transporter works from a phone. The sidebar's
   navigation is reduced to a row of links, `.app-nav`: up to thirteen of them,
   depending on the branch's company's profiles (templates/base.html says which
   and why), followed by the session's scope and the account's buttons.

   All of that after the brand sits in `.app-menu`, a native <details>. Mobile
   first: up to 1024px it is a «Menú» button whose panel drops below the band
   as one column, so nothing scrolls sideways and no label is cut. From
   1025px, where the browser can style `::details-content`, the summary is
   hidden and the panel is kept shown while the element is closed, laid out
   as a row beside the brand. A browser that cannot style it keeps the menu
   at every width: a click more on a desktop, rather than a row it could not
   show. The cut is the header's own: the other `max-width: 720px` queries
   in this file are for the tables, the landing page and the forms, and
   stay where they are.

   Why 1024px. The cut used to be 720px, the one this file uses for its
   grids, and from 721px the row was shown and wrapped. Measured in headless
   Chrome on 2026-10-04, the band then stood, at 721, 768, 820 and 1024px:
     - a carrier's branch:                     167, 167, 167, 136px;
     - a branch holding every profile:         167, 167, 167, 167px;
     - a master inside such a branch:          211, 211, 211, 167px;
     - a sistema de gestión's branch:          136, 136, 136, 151px;
     - a generator's branch:                   167, 136, 136, 145px.
   On a tablet held upright that is a sixth to a quarter of the screen
   before any content, on every page, and «Cerrar sesión» could end up
   alone on a third line. With the menu up to 1024px the band is 64px at
   every one of those widths, for every one of those sessions, and the
   page still never scrolls sideways. Keeping the cut at 720px was rejected
   for those heights. From 1025px to 1440px the same sessions get a band of
   106 to 167px, two to four lines counting the brand's, which a laptop's
   wider and taller screen absorbs where a tablet's does not.

   The open panel's width. On a phone (up to 720px) it is a column as wide
   as the band. From 721px it is a 360px dropdown hanging from the band's
   right edge, under «Menú», with the same height cap and the same
   scrolling inside it. 360px is the narrowest round width that keeps every
   fixed label on one line: the longest is the scope's «como cuenta
   delegada · Prevencionista de riesgos», 292px at 13px (measured in
   headless Chrome on 2026-10-04), and the panel takes 16px of padding on
   each side and a 1px border, leaving 327px; a browser whose scrollbars
   take room still leaves about 312px. Only a branch name longer than that
   wraps, as it does on a phone; the links and buttons are far narrower
   («Notas operativas», the widest link, is 121px). Measured the same day
   with the menu open at 721x600, 768x1024, 820x1180, 1024x768 and
   1024x1366, for all five sessions above, on «Inicio», «Ubicación» and
   «Mapa»: the panel is 360px wide against the window's right edge, no
   control or scope text is cut, the page never scrolls sideways, and
   every link and button, hit-tested at its centre, is the element there,
   over both maps. A browser that cannot style `::details-content` keeps
   this dropdown above 1024px too, since the rule is not inside the
   desktop row's guard; the row's own rules give the width back.
   Rejected: the phone's full-width column on a tablet as well. At 1024px
   it is a 1,024px sheet holding labels of at most 292px, more than two
   thirds of it empty, laid over the whole page -- on «Mapa», the whole
   map -- for a list a finger reaches just as well at 360px; a dropdown
   from the button that opened it also reads as that button's menu.

   The menu closes only by tapping «Menú» again, or by following one
   of its links, which loads another page. Escape and a tap outside the panel
   leave it open: that is how a native <details> disclosure behaves, and
   making it close on either would take a script, which this frame does not
   load (see the last rejected alternative below). It is the WAI-ARIA
   Authoring Practices' disclosure pattern, which asks for no Escape
   handling, and it meets WCAG: the control is keyboard-operable (Enter and
   Space toggle it), named by its text and exposes its expanded state, and
   the panel is not a modal that traps focus or hides the page from a
   screen reader. This is a choice, not an oversight; adding a script for it
   would be a decision to load JavaScript on every page.

   The open panel is positioned (z-index 10) and lies over the page, which
   on two pages holds a Leaflet map whose panes reach z-index 400 and whose
   controls reach 1000. The maps are isolated (`.leaflet-container`, with the
   heat map, below) so all of Leaflet's layers paint as one layer at the
   page's level, under the panel, rather than the panel's z-index being
   raised to outbid Leaflet's numbers.

   Rejected for the phone:
     - scrolling `.app-nav` sideways inside the band: beside the wordmark
       a phone shows only a few of up to thirteen links, with no cue that
       more exist, and the scope and the buttons still need the band's width;
     - wrapping the row onto several lines: the row needs about 1,500px for
       a carrier's branch, so at phone width it becomes a header of several
       lines on every page, pushing down everything the page is for (wider
       screens do wrap it, below, where it takes two to four lines);
     - a script-driven toggle: the frame loads no script, and <details> is
       already a keyboard-operable button that a screen reader announces with
       its expanded state, with nothing to wire up.

   Above the cut the row wraps instead of overflowing. Measured in headless
   Chrome on 2026-10-04, one line holding the whole wordmark and every label
   untruncated needs about 1,470px for a sistema de gestión's branch, 1,560px
   for a generator's, 1,680px for a carrier's, 2,060px for a branch holding
   every profile and 2,230px for a master administrator inside one (who also
   gets «Salir de la sucursal»); the figure also grows with the branch's
   name. Before the row wrapped it never took that room: the brand was
   allowed to shrink to nothing, so the links ran over the wordmark, the
   scope was cut with an ellipsis, and still the page scrolled sideways --
   to 1,246px for a carrier's branch, 1,578px for every profile and 1,734px
   for the master -- at any window narrower than that, a 768-820px tablet
   included. Now:
     - the brand never shrinks below the logo and the wordmark, so nothing
       paints over it;
     - when the brand and the row do not fit on one line, the row moves to
       a line of its own under the brand, still hugging the right edge as
       the single row does;
     - when the row alone is wider than the window, it wraps again inside,
       the links first and the scope and the buttons after them, and the
       links themselves wrap as a last resort;
     - the band keeps its 64px when everything fits on one line, so a
       header that fits -- a session outside any branch, or a branch on a
       wide enough screen -- looks exactly as before, control for control.
   The cost is a taller band, of two to four lines counting the brand's, on
   most laptops for a session inside a branch: the row did not fit them
   before either, it was only hidden under the wordmark and the ellipsis.
   The account's four buttons -- «Mapa», «Catastro», «Seguridad» and
   «Cerrar sesión» -- are one group (`.app-account`) that does not wrap
   inside, so when the row runs out of room they move to the next line
   together and «Cerrar sesión» is never left alone on a line of its own,
   as it was at 1,280px for a generator's and a sistema de gestión's branch
   and at 1,440px for a carrier's. In the menu's column the group is
   invisible: four more rows.
   Rejected for the desktop:
     - raising the cut past 1024px, so the menu covers more widths: no width
       covers every branch, since the row's length depends on the company's
       profiles, the session's role and the branch's name, so wrapping is
       needed as the fallback anyway; and a cut wide enough for the
       master's 2,230px would hide the navigation behind a click on every
       common desktop, where two lines show all of it;
     - truncating or scrolling the links sideways: the same objections as on
       the phone, a link a user cannot see is a link they do not know of.
   ------------------------------------------------------------------------- */

.app-header {
  /* The menu's panel is positioned against the band, below it. */
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  height: 64px;
  padding: 0 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--brand-100);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex: 1;
  text-decoration: none;
}

.brand img {
  width: 44px;
  height: 44px;
  object-fit: contain;
  flex-shrink: 0;
}

.brand span {
  font-weight: 700;
  font-size: 17px;
  color: var(--brand-900);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-header form {
  margin: 0;
  flex-shrink: 0;
}

/* The narrow layout, which is also every width's where `::details-content`
   cannot be styled. The summary drops its disclosure triangle: the word
   «Menú» is its label, and `.btn-quiet` already makes it look pressable. */
.app-menu {
  flex-shrink: 0;
}

.app-menu > summary {
  list-style: none;
}

.app-menu > summary::-webkit-details-marker {
  display: none;
}

/* One column below the band, as wide as the band on a phone (from 721px it
   narrows to 360px, below), scrolling down within the viewport when every
   profile's links do not fit the screen's height. Each label wraps rather
   than being cut: the scope's ellipsis is for the desktop row. */
.app-menu-panel {
  position: absolute;
  top: 100%;
  right: 0;
  left: 0;
  /* Over the page's own content. A map stays under it because every
     Leaflet map is isolated (`.leaflet-container`), not because 10 outbids
     Leaflet's z-indices, which it does not. */
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
  max-height: calc(100vh - 64px);
  /* A phone's browser bar shrinks the viewport `vh` ignores; `dvh` follows
     it where known, and the line above stands where it is not. */
  max-height: calc(100dvh - 64px);
  overflow-y: auto;
  padding: 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--brand-100);
  box-shadow: 0 12px 16px -8px rgba(0, 0, 0, 0.12);
}

.app-menu-panel .app-nav {
  flex-direction: column;
  gap: 0;
}

/* 44px rows, a finger's reach, for the links a phone taps. */
.app-menu-panel .app-nav a {
  padding: 11px 0;
  font-size: 15px;
}

.app-menu-panel .session-scope {
  text-align: left;
}

.app-menu-panel .session-scope strong,
.app-menu-panel .session-scope span {
  white-space: normal;
  overflow-wrap: anywhere;
}

.app-menu-panel .btn,
.app-menu-panel form {
  width: 100%;
}

/* The account's four buttons. In the menu's column they are four more rows,
   spaced as the panel spaces its rows; the group only matters on the desktop
   row, below. */
.app-account {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* From 721px the open panel is a 360px dropdown hanging from the band's
   right edge, under «Menú», instead of a column across the whole window
   (the header comment gives the measurements and the rejected
   alternative). Its height cap, its own scrolling and its z-index are the
   phone's, above. The query is not inside the desktop row's guard, so a
   browser that keeps the menu at every width gets the same dropdown on a
   desktop too; where the row is shown, the row's rules below undo the
   width. The left border and the softer, wider shadow mark the panel's
   left edge, which on a phone was the window's. */
@media (min-width: 721px) {
  .app-menu-panel {
    left: auto;
    width: 360px;
    border-left: 1px solid var(--brand-100);
    border-bottom-left-radius: 8px;
    box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.16);
  }
}

/* The desktop row. Closed, a <details> renders its content with
   `content-visibility: hidden` on `::details-content`; overriding that is
   what keeps the panel shown, and only a browser that knows the
   pseudo-element can be asked to, hence the `@supports` guard. The wrapping
   rules sit inside the same guard because only there is the row shown; a
   browser without it shows the brand and «Menú», which always fit. */
@media (min-width: 1025px) {
  @supports selector(::details-content) {
    /* Wraps instead of overflowing (the header comment says why). The band
       grows past 64px only when it wraps: 8px above and below plus the
       hairline leave 47px, so a single line is centred exactly where the
       fixed 64px band centred it. */
    .app-header {
      flex-wrap: wrap;
      row-gap: 8px;
      height: auto;
      min-height: 64px;
      padding-top: 8px;
      padding-bottom: 8px;
    }

    /* Never narrower than the logo and the wordmark: the shrinking that let
       the links run over them is what this undoes. It still grows, so on a
       line that fits it pushes the row to the right edge, as before. */
    .brand {
      flex: 1 0 auto;
    }

    /* `margin-left: auto` keeps the row at the right edge when it has moved
       to its own line under the brand; `min-width: 0` lets it shrink to
       that line's width and wrap inside, rather than overflow it. */
    .app-menu {
      flex: 0 1 auto;
      min-width: 0;
      margin-left: auto;
    }

    .app-menu > summary {
      display: none;
    }

    .app-menu::details-content {
      content-visibility: visible;
    }

    /* `width: auto` undoes the tablet dropdown's 360px, which would
       otherwise squeeze the whole row into it; `border: 0` drops its left
       border, and with no border, background or shadow its corner radius
       paints nothing. */
    .app-menu-panel {
      position: static;
      width: auto;
      flex-direction: row;
      flex-wrap: wrap;
      justify-content: flex-end;
      align-items: center;
      gap: 8px 12px;
      max-height: none;
      overflow: visible;
      padding: 0;
      background: none;
      border: 0;
      box-shadow: none;
    }

    .app-menu-panel .app-nav {
      flex-direction: row;
      flex-wrap: wrap;
      justify-content: flex-end;
      gap: 8px 16px;
    }

    .app-menu-panel .app-nav a {
      padding: 0;
      font-size: 14px;
    }

    .app-menu-panel .session-scope {
      text-align: right;
    }

    .app-menu-panel .session-scope strong,
    .app-menu-panel .session-scope span {
      white-space: nowrap;
    }

    .app-menu-panel .btn,
    .app-menu-panel form {
      width: auto;
    }

    /* One unit that never wraps inside, so when the row runs out of room
       the four buttons move to the next line together, and «Cerrar sesión»
       is never left alone on a line of its own. It is 373px wide, far
       less than any window this layout applies to. */
    .app-menu-panel .app-account {
      flex-direction: row;
      align-items: center;
    }
  }
}

.app-main {
  width: 100%;
  max-width: 1024px;
  margin: 0 auto;
  padding: 24px 16px;
}

/* ---------------------------------------------------------------------------
   Card and buttons.
   ------------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border-radius: 16px;
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.12);
  padding: 32px 40px;
}

.btn {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border: 0;
  border-radius: 8px;
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: background 150ms;
}

.btn-primary {
  width: 100%;
  color: #ffffff;
  background: var(--brand-600);
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}

.btn-primary:hover {
  background: var(--brand-700);
}

/* The header's buttons: the sidebar's secondary action style, small enough
   that the phone's «Menú» shares a 64px band with the wordmark. */
.btn-quiet {
  padding: 7px 12px;
  font-size: 13px;
  color: var(--text);
  background: transparent;
  border: 1px solid #d1d5db;
}

.btn-quiet:hover {
  background: #f0fdf4;
}

.btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--brand-500);
}

/* ---------------------------------------------------------------------------
   Login screen: src/app/login/page.tsx without the demo-user panel, the Google
   button and the /register and /demo links, none of which has a flow behind
   it in this repository.
   ------------------------------------------------------------------------- */

.auth-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 48px 16px;
  background: linear-gradient(to bottom right, #f0fdf4, #ffffff, #eff6ff);
}

.auth-head {
  width: 100%;
  max-width: 448px;
  text-align: center;
}

.auth-head img {
  width: 72px;
  height: 72px;
  object-fit: contain;
  filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.12));
}

.auth-head .wordmark {
  display: block;
  margin-top: 8px;
  font-weight: 700;
  font-size: 28px;
  color: var(--brand-900);
  letter-spacing: -0.02em;
}

.auth-head h1 {
  margin: 16px 0 0;
  font-size: 30px;
  font-weight: 700;
  color: var(--text-strong);
}

.auth-head p {
  margin: 8px 0 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--text);
}

.auth-body {
  width: 100%;
  max-width: 448px;
  margin-top: 32px;
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.field label {
  display: block;
  margin-bottom: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-strong);
}

.field-control {
  position: relative;
}

.field-control svg {
  position: absolute;
  top: 50%;
  left: 12px;
  transform: translateY(-50%);
  pointer-events: none;
}

.field-control input {
  width: 100%;
  padding: 12px 12px 12px 40px;
  font-size: 14px;
  background: var(--surface-muted);
  border: 0;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}

.field-control input:focus {
  outline: none;
  background: var(--surface);
  box-shadow: 0 0 0 2px var(--brand-500);
}

.alert-error {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  color: var(--error-text);
  background: var(--error-bg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}

.alert-error svg {
  flex-shrink: 0;
}

/* V4's cards keep 40px of side padding; on a phone that leaves the form too
   narrow, so it tightens below the width of a small tablet. */
@media (max-width: 480px) {
  .card {
    padding: 24px 20px;
  }

  .auth-page {
    padding-top: 32px;
  }

  .auth-head h1 {
    font-size: 26px;
  }
}

/* ---------------------------------------------------------------------------
   Public landing page (templates/landing.html): src/app/page.tsx's inline
   values as classes. Its 3-column grids, 2-column panel and 3-column figures
   collapse to one column below a small tablet's width, where V4 overflowed
   instead. Each card's own colours stay inline in the template, as V4 kept
   them in its data arrays.

   **Every grid track is `minmax(0, 1fr)`, never a bare `1fr`.** A bare
   `1fr` is `minmax(auto, 1fr)`: the track may not shrink below its
   content's min-content width. The first cut used it, and on a phone the
   one-column panel grew to the width of the figures box inside it (the
   three figures side by side, «SINADER» at 24px bold, the 48px «G → T →
   G»), so the page was 393px wide at a 320, 360 and 390px viewport and
   scrolled sideways. With `minmax(0, 1fr)` a track is as wide as the
   container gives it and its content wraps; the phone rules below then
   make what cannot wrap (the chain line, the figures) small enough to fit
   a 320px viewport, measured in headless Chrome at 320, 360 and 390px
   with no horizontal scroll.
   ------------------------------------------------------------------------- */

.landing {
  min-height: 100vh;
  background: linear-gradient(to bottom right, #ecfdf5, #ffffff, #eff6ff);
}

.landing-wrap {
  max-width: 1280px;
  margin: 0 auto;
}

.landing-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(6px);
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}

.landing-header-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 16px 24px;
}

.landing-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 20px;
  color: var(--brand-900);
  letter-spacing: -0.01em;
}

.landing-brand img {
  width: 44px;
  height: 44px;
  object-fit: contain;
}

.landing-link {
  padding: 8px 24px;
  font-weight: 500;
  color: var(--brand-700);
  text-decoration: none;
  transition: color 150ms;
}

.landing-link:hover {
  color: #065f46;
}

.landing-hero {
  padding: 80px 24px;
  text-align: center;
}

.landing-hero-logo {
  display: flex;
  justify-content: center;
  margin-bottom: 32px;
}

.landing-hero-logo img {
  width: 140px;
  height: 140px;
  object-fit: contain;
  filter: drop-shadow(0 0 48px rgba(52, 211, 153, 0.35));
}

.landing-hero h1 {
  margin: 0 0 24px;
  font-size: clamp(2.25rem, 1rem + 4vw, 3.75rem);
  font-weight: 700;
  line-height: 1.1;
  color: var(--text-strong);
}

.landing-hero h1 span {
  color: var(--brand-600);
}

.landing-lead {
  max-width: 720px;
  margin: 0 auto 32px;
  font-size: 20px;
  color: var(--text-muted);
}

.landing-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px;
}

.landing-btn {
  display: inline-block;
  padding: 16px 32px;
  border-radius: 12px;
  font-weight: 600;
  font-size: 18px;
  text-decoration: none;
  transition: background 150ms, box-shadow 150ms;
}

.landing-btn-primary {
  color: #ffffff;
  background: var(--brand-600);
}

.landing-btn-primary:hover {
  background: var(--brand-700);
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.15);
}

.landing-btn-light {
  font-weight: 700;
  color: var(--brand-600);
  background: #ffffff;
}

.landing-btn-light:hover {
  background: #f3f4f6;
}

.landing-btn:focus-visible,
.landing-link:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}

.landing-section {
  padding: 80px 24px;
}

.landing-section-tint {
  background: rgba(255, 255, 255, 0.5);
}

.landing-section-muted {
  background: var(--surface-muted);
}

.landing-section-head {
  margin-bottom: 64px;
  text-align: center;
}

.landing-section h2 {
  margin: 0 0 16px;
  font-size: 36px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--text-strong);
}

.landing-section-head p {
  max-width: 800px;
  margin: 0 auto;
  font-size: 20px;
  color: var(--text-muted);
}

.landing-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 32px;
}

.landing-feature,
.landing-product {
  border-radius: 16px;
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.08);
  transition: box-shadow 200ms;
}

.landing-feature {
  padding: 32px;
  background: var(--surface);
}

.landing-product {
  padding: 24px;
}

.landing-feature:hover,
.landing-product:hover {
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.12);
}

.landing-feature-icon,
.landing-product-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
}

.landing-feature-icon {
  width: 56px;
  height: 56px;
  margin-bottom: 24px;
}

.landing-product-icon {
  width: 48px;
  height: 48px;
  margin-bottom: 16px;
}

.landing-feature h3,
.landing-product h3 {
  margin: 0 0 12px;
  font-size: 20px;
  font-weight: 700;
  color: var(--text-strong);
}

.landing-feature p,
.landing-product p {
  margin: 0;
  color: var(--text-muted);
}

.landing-product p {
  font-size: 14px;
}

.landing-note {
  margin-top: 48px;
  padding: 24px;
  background: #eff6ff;
  border-left: 4px solid #2563eb;
  border-radius: 8px;
}

.landing-note h4 {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 700;
  color: var(--text-strong);
}

.landing-note p {
  margin: 0;
  color: var(--text);
}

.landing-panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 48px;
  align-items: center;
  padding: 64px 48px;
  color: #ffffff;
  background: linear-gradient(to bottom right, #059669, #2563eb);
  border-radius: 24px;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}

.landing-panel h2 {
  margin-bottom: 24px;
  color: #ffffff;
}

.landing-checks {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.landing-checks li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 18px;
}

.landing-checks svg {
  flex-shrink: 0;
  margin-top: 4px;
}

.landing-panel-box {
  padding: 32px;
  text-align: center;
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 16px;
}

.landing-chain {
  margin-bottom: 8px;
  font-size: 48px;
  font-weight: 700;
}

.landing-chain-label {
  margin: 0 0 32px;
  font-size: 20px;
}

.landing-figures {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  margin: 0 0 32px;
}

.landing-figures dt {
  font-size: 30px;
  font-weight: 700;
}

.landing-figures dd {
  margin: 0;
  font-size: 14px;
  opacity: 0.9;
}

.landing-closing {
  max-width: 900px;
  text-align: center;
}

.landing-closing > p {
  margin: 0 0 32px;
  font-size: 20px;
  color: var(--text-muted);
}

.landing-footer {
  padding: 48px 24px;
  color: #ffffff;
  background: var(--text-strong);
}

.landing-footer-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 32px;
}

.landing-footer-brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.landing-footer-brand img {
  width: 40px;
  height: 40px;
  object-fit: contain;
}

.landing-footer-brand strong {
  display: block;
  font-size: 18px;
}

.landing-footer-brand span {
  font-size: 14px;
  color: var(--placeholder);
}

.landing-footer-legal p {
  margin: 0;
  color: var(--placeholder);
}

.landing-footer-legal p + p {
  margin-top: 8px;
  font-size: 14px;
  color: var(--text-subtle);
}

@media (max-width: 720px) {
  .landing-grid,
  .landing-panel,
  .landing-figures {
    grid-template-columns: minmax(0, 1fr);
  }

  .landing-figures {
    gap: 16px;
  }

  .landing-panel-box {
    padding: 24px 16px;
  }

  .landing-chain {
    font-size: 32px;
  }

  .landing-chain-label {
    font-size: 17px;
  }

  .landing-checks li {
    font-size: 16px;
  }

  .landing-hero,
  .landing-section {
    padding: 56px 16px;
  }

  .landing-header-row {
    padding: 12px 16px;
  }

  .landing-link {
    padding: 8px 0;
  }

  .landing-section-head {
    margin-bottom: 40px;
  }

  .landing-section h2 {
    font-size: 28px;
  }

  .landing-panel {
    padding: 40px 24px;
  }

  .landing-figures dt {
    font-size: 24px;
  }
}

/* ---------------------------------------------------------------------------
   Home page.
   ------------------------------------------------------------------------- */

.page-title {
  margin: 0 0 16px;
  font-size: 24px;
  font-weight: 700;
  color: var(--text-strong);
}

.session-identity {
  margin: 0;
  font-size: 16px;
  color: var(--text);
}

.session-identity strong {
  color: var(--text-strong);
}

.session-email {
  margin: 4px 0 0;
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--text-subtle);
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------------
   Branch scope: which branch the session acts for, in the header, and the
   list a user enters a branch from on the home page. In the desktop row the
   header text keeps to one line per part, and is truncated only in the
   unlikely case that the scope alone is wider than a wrapped line of the
   row; on a phone or a tablet it sits in the menu's panel, where
   `.app-menu-panel` lets it wrap instead.
   ------------------------------------------------------------------------- */

.session-scope {
  display: flex;
  flex-direction: column;
  min-width: 0;
  margin: 0;
  font-size: 13px;
  line-height: 1.2;
  text-align: right;
  color: var(--text-subtle);
}

.session-scope strong,
.session-scope span {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.session-scope strong {
  color: var(--text-strong);
}

.card + .card {
  margin-top: 16px;
}

.section-title {
  margin: 0 0 12px;
  font-size: 18px;
  font-weight: 700;
  color: var(--text-strong);
}

.branch-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.branch-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 0;
  border-top: 1px solid #e5e7eb;
}

.branch-list small {
  display: block;
  color: var(--text-subtle);
}

.branch-list form {
  margin: 0;
  flex-shrink: 0;
}

/* ---------------------------------------------------------------------------
   The master administrator's corporate view: the consolidated inventory, its
   breakdown per branch, and one branch's lots. V4's «vista maestra» was not
   ported, so these are plain tables in the frame's colours. A real <table>
   rather than the movement list's grid rows because these are figures read
   across columns, and a screen reader needs the header cells to say which.
   The wrapper scrolls sideways on a phone instead of squeezing the numbers.
   ------------------------------------------------------------------------- */

.subsection-title {
  margin: 20px 0 8px;
  font-size: 15px;
  font-weight: 700;
  color: var(--text-strong);
}

.table-scroll {
  overflow-x: auto;
}

.inventory-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  color: var(--text-strong);
}

.inventory-table th,
.inventory-table td {
  padding: 10px 12px;
  text-align: left;
  border-top: 1px solid #e5e7eb;
  white-space: nowrap;
}

.inventory-table thead th {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
  background: var(--surface-muted);
  border-top: 0;
}

.inventory-table .num {
  text-align: right;
}

.inventory-table tfoot th,
.inventory-table tfoot td {
  font-weight: 700;
  border-top: 2px solid var(--border);
}

.inventory-table form {
  margin: 0;
}

/* ---------------------------------------------------------------------------
   Navigation and flash messages.
   ------------------------------------------------------------------------- */

.app-nav {
  display: flex;
  gap: 16px;
}

.app-nav a {
  font-size: 14px;
  font-weight: 600;
  color: var(--brand-700);
  text-decoration: none;
}

.app-nav a:hover {
  color: var(--brand-900);
}

.flash-list {
  margin: 0 0 16px;
  padding: 0;
  list-style: none;
}

.flash {
  padding: 12px 16px;
  border-radius: 10px;
  font-size: 14px;
  color: #065f46;
  background: #eef9f2;
  border: 1px solid var(--brand-200);
}

/* ---------------------------------------------------------------------------
   Movements: V4's generator «Movimientos» page, its «Registrar Movimiento»
   and «Ficha» modals, and the carrier's mobile scan screen. Values are the
   ones those screens set inline (src/app/dashboard/generador/movimientos,
   src/components/generador-v3/modales, src/components/transportista-v3).
   ------------------------------------------------------------------------- */

.mono {
  font-family: var(--font-mono);
}

.num {
  text-align: right;
}

.muted,
.empty {
  color: var(--placeholder);
}

.empty {
  padding: 48px 20px;
  margin: 0;
  text-align: center;
  font-size: 14px;
}

.hint {
  display: block;
  margin: 6px 0 16px;
  font-size: 11px;
  line-height: 1.5;
  color: var(--placeholder);
}

.btn-inline {
  width: auto;
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}

.btn-quiet {
  text-decoration: none;
}

.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
  padding: 20px 24px;
  border-radius: 14px;
  background: linear-gradient(to right, var(--brand-50), var(--brand-100));
  border: 1px solid var(--brand-200);
}

.page-head .page-title {
  margin: 0;
  color: var(--brand-900);
}

.page-lede {
  margin: 2px 0 0;
  font-size: 14px;
  font-weight: 500;
  color: var(--brand-700);
}

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  margin-bottom: 24px;
  padding: 16px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
}

.filter-bar input,
.rm-doc input,
.rm-doc select,
.rm-doc textarea {
  width: 100%;
  padding: 9px 11px;
  font-size: 14px;
  background: var(--surface);
  border: 1px solid #d1d5db;
  border-radius: 8px;
  outline: none;
}

.filter-bar input {
  flex: 1;
  min-width: 220px;
  width: auto;
}

.filter-bar input:focus,
.rm-doc input:focus,
.rm-doc select:focus {
  border-color: var(--brand-600);
  box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.12);
}

.movement-table {
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
}

.movement-row {
  display: grid;
  grid-template-columns: 1.1fr 0.8fr 1.5fr 0.9fr 1.4fr 1.1fr;
  gap: 12px;
  align-items: center;
  padding: 14px 20px;
  font-size: 13px;
  color: var(--text-strong);
  text-decoration: none;
  border-bottom: 1px solid #f3f4f6;
  transition: background 150ms;
}

a.movement-row:hover {
  background: var(--surface-muted);
}

.movement-row small {
  display: block;
  font-size: 11px;
  color: var(--placeholder);
}

.movement-row .kind {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--brand-700);
}

.movement-row-head {
  padding: 12px 20px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
  background: var(--surface-muted);
}

.pager {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: center;
  margin-top: 16px;
  font-size: 13px;
  color: var(--text-subtle);
}

.note {
  margin: 24px 0 0;
  padding: 14px 18px;
  font-size: 13px;
  line-height: 1.5;
  color: #065f46;
  background: #eef9f2;
  border: 1px solid var(--brand-200);
  border-radius: 10px;
}

/* The semáforo: V4's ESTADOS table, one colour set per MovementStatus. */
.status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  border-radius: 9999px;
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 9999px;
  background: currentColor;
}

.status-published { color: #1e40af; background: #dbeafe; }
.status-published .status-dot { background: #3b82f6; }
.status-in_transit { color: #b45309; background: #fef3c7; }
.status-in_transit .status-dot { background: #d97706; }
.status-delivered { color: #4d7c0f; background: #ecfccb; }
.status-delivered .status-dot { background: #84cc16; }
.status-delivered_settled { color: #b91c1c; background: #fee2e2; }
.status-delivered_settled .status-dot { background: #dc2626; }
.status-complete { color: #15803d; background: #dcfce7; }
.status-complete .status-dot { background: #16a34a; }
/* A withdrawn need (Movement.withdraw()): grey, because V4's ESTADOS table
   has no such state to borrow a colour from -- the sixth state is a
   provisional decision -- and a withdrawn need asks nobody for anything. */
.status-withdrawn { color: #475569; background: #f1f5f9; }
.status-withdrawn .status-dot { background: #94a3b8; }

/* The carrier's own leg (CarrierLeg), in place of the semáforo on its pages:
   the same badge, four colour sets. Amber for a load on the road, as the
   semáforo's «En tránsito»; green once delivered, with no further shade,
   because whether a CDF followed is not the carrier's to read; grey for a
   load refused at destination -- the leg is over, and nothing about it is
   the carrier's to act on (the follow-up is the origin's), so it is not the
   red the origin's refusal mark uses while someone must act. */
.leg-pending_load { color: #1e40af; background: #dbeafe; }
.leg-pending_load .status-dot { background: #3b82f6; }
.leg-in_transit { color: #b45309; background: #fef3c7; }
.leg-in_transit .status-dot { background: #d97706; }
.leg-delivered { color: #15803d; background: #dcfce7; }
.leg-delivered .status-dot { background: #16a34a; }
.leg-rejected { color: #374151; background: #e5e7eb; }
.leg-rejected .status-dot { background: #6b7280; }

/* The carrier's leg filters: links, so the list works without JavaScript. */
.leg-filters a[aria-current="page"] {
  color: var(--surface);
  background: var(--brand-700);
}

/* A refused load, beside the semáforo rather than inside it (see
   _rejection_mark.html). Red while it waits for an alternative destination,
   because that is the one state someone must act on; neutral once answered. */
.rejection-mark {
  display: inline-flex;
  margin: 4px 0 0 6px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  background: #f3f4f6;
  border-radius: 9999px;
}

.rejection-mark-pending {
  color: #991b1b;
  background: #fee2e2;
}

/* The refusal on the ficha: the notice the generator receives. */
.rejection-notice {
  margin: 16px 0;
  padding: 14px 18px;
  color: #7f1d1d;
  background: #fef2f2;
  border: 1px solid #fca5a5;
  border-radius: 10px;
}

.rejection-reason {
  margin: 4px 0 12px;
  font-weight: 600;
}

.kind-badge {
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  background: #f3f4f6;
  border-radius: 9999px;
}

/* The document itself: header with the issuer and the red folio box, the
   lines in a dark-banded table, the stamp at the foot. */
.rm-doc {
  max-width: 720px;
  margin: 0 auto;
  padding: 24px 32px 28px;
  background: var(--surface);
  border-radius: 14px;
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.18);
}

.rm-eyebrow,
.rm-label,
.rm-doc label,
.rm-facts dt,
.rm-line-head {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.rm-eyebrow {
  margin: 0 0 18px;
  color: var(--brand-700);
}

.rm-kind {
  display: flex;
  gap: 10px;
  margin: 0 0 22px;
  padding: 0;
  border: 0;
}

.rm-doc .rm-kind-option {
  flex: 1;
  display: block;
  padding: 12px 14px;
  text-transform: none;
  letter-spacing: 0;
  border: 2px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
}

.rm-kind-option:has(input:checked) {
  border-color: var(--brand-600);
  background: var(--brand-50);
}

.rm-doc .rm-kind-option input {
  width: auto;
  margin: 0 6px 0 0;
}

.rm-kind-option strong {
  font-size: 13px;
  color: var(--text-strong);
}

.rm-kind-option small {
  display: block;
  font-size: 11px;
  font-weight: 400;
  line-height: 1.35;
  color: var(--text-subtle);
}

.rm-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 18px;
  border-bottom: 2px solid var(--brand-900);
}

.rm-issuer {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.rm-issuer strong,
.rm-issuer span,
.rm-issuer small {
  display: block;
}

.rm-issuer strong {
  font-size: 15px;
}

.rm-issuer span {
  font-size: 12px;
  color: var(--text-subtle);
}

.rm-issuer small {
  font-size: 11px;
  color: var(--placeholder);
}

.rm-folio {
  padding: 10px 16px;
  text-align: center;
  color: #dc2626;
  border: 2px solid #dc2626;
  border-radius: 8px;
}

.rm-folio small,
.rm-folio span {
  display: block;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.rm-folio span {
  margin: 6px 0 4px;
  font-size: 11px;
}

.rm-folio strong {
  font-size: 18px;
  font-weight: 800;
}

.rm-grid,
.rm-facts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 20px;
  margin: 18px 0;
}

.rm-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.rm-doc label,
.rm-facts dt {
  color: var(--placeholder);
}

.rm-facts dd {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--text);
}

.field-error {
  font-size: 12px;
  color: var(--error-text);
}

.has-error input,
.has-error select {
  border-color: #fca5a5;
}

.alert-error ul {
  margin: 0;
  padding-left: 18px;
}

.rm-lines {
  overflow: hidden;
  margin: 18px 0 8px;
  border: 1px solid var(--border);
  border-radius: 10px;
}

.rm-line {
  display: grid;
  grid-template-columns: 2fr 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 12px 14px;
  font-size: 13px;
  border-top: 1px solid #f3f4f6;
}

.rm-line-head {
  padding: 9px 14px;
  font-size: 10px;
  color: var(--brand-100);
  background: var(--brand-900);
  border-top: 0;
}

.rm-line-foot {
  font-size: 12px;
  color: var(--text-subtle);
  background: var(--surface-muted);
}

.rm-line-errors {
  grid-column: 1 / -1;
  margin: 0;
  padding-left: 18px;
  font-size: 12px;
  color: var(--error-text);
}

/* A need's photographs: a row of thumbnails of one fixed height, each keeping
   its proportions (the img's width and height attributes give the browser the
   ratio) and cropped to a square-ish box at most. Used on the ficha, the offer
   page and each row of the market's list. */
.need-photos {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

.need-photos img {
  display: block;
  width: auto;
  height: 72px;
  max-width: 120px;
  object-fit: cover;
  border-radius: 6px;
}

.rm-photos-section .rm-label,
.rm-photos-section .empty,
.rm-photos-section .hint,
.rm-photos-section form,
.rm-photos-section .need-photos {
  padding: 12px 14px 0;
}

.rm-photos-section form {
  padding-bottom: 12px;
}

/* The offers received on a need's ficha: who offers what on the left, the
   answer (or the two buttons that give it) on the right. */
.rm-offers .rm-label,
.rm-offers .hint {
  padding: 12px 14px 0;
}

.rm-offer {
  grid-template-columns: 1fr auto;
}

.rm-offer small {
  display: block;
  margin-top: 4px;
  color: var(--text-subtle);
}

.inline-form {
  display: inline-block;
  margin: 0 0 0 6px;
}

.offer-answer {
  display: block;
  font-weight: 600;
}

.rm-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 18px 0;
}

.rm-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px dashed #d1d5db;
}

.rm-label {
  margin: 0 0 8px;
  color: var(--text-subtle);
}

.scan-list {
  margin: 0;
  padding: 0;
  font-size: 12px;
  list-style: none;
}

.rm-stamp {
  max-width: 180px;
  text-align: center;
}

.rm-stamp svg {
  display: block;
  margin: 0 auto;
  border: 1px solid var(--border);
  border-radius: 6px;
}

.rm-stamp small {
  display: block;
  margin-top: 4px;
  font-size: 10px;
  line-height: 1.3;
  color: var(--placeholder);
}

.rm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid #f3f4f6;
}

/* The carrier's phone screen. */
.scan-screen {
  max-width: 420px;
  margin: 0 auto;
}

.scan-card {
  overflow: hidden;
  margin-bottom: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
}

.scan-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 14px;
  font-size: 12px;
  color: #dc2626;
  border-bottom: 2px solid var(--brand-900);
}

.scan-card-body {
  padding: 14px;
}

.scan-waste {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
}

.scan-route {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--text-subtle);
}

.scan-button {
  padding: 16px;
  font-size: 16px;
  border-radius: 12px;
}

.scan-unload {
  background: var(--brand-900);
}

.scan-done {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 16px;
  text-align: center;
  color: #4d7c0f;
  background: #ecfccb;
  border-radius: 12px;
}

.scan-done small {
  font-size: 12px;
}

/* ---------------------------------------------------------------------------
   The gestor plant's ficha de acreditación. The summary is V4's compact
   «Autorizaciones» card on the gestor dashboard
   (src/components/gestor-v3/secciones/dashboard.tsx): label and value rows,
   the certified codes as emerald chips. V4 has no screen that edits it; the
   form below the card reuses the movement form's fields.
   ------------------------------------------------------------------------- */

.acc-summary {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 18px 0;
  padding: 20px;
  border: 1px solid var(--border);
  border-radius: 12px;
}

.acc-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.acc-row > span:first-child {
  font-size: 12px;
  color: #6b7280;
}

.acc-value {
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}

.acc-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 4px;
}

.acc-chip {
  padding: 1px 7px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: #065f46;
  background: #d1fae5;
  border-radius: 6px;
}

.acc-treatments {
  margin: 18px 0 0;
  padding: 0;
  border: 0;
}

.acc-treatments legend {
  margin-bottom: 8px;
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
}

.acc-options {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 20px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.acc-treatments label {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  font-size: 13px;
  color: var(--text);
}

/* The desktop grids stack on a phone rather than scroll sideways. */
@media (max-width: 720px) {
  .movement-row,
  .rm-grid,
  .rm-facts,
  .acc-options {
    grid-template-columns: 1fr;
  }

  .movement-row-head {
    display: none;
  }

  .rm-doc {
    padding: 20px 16px;
  }

  .rm-kind {
    flex-direction: column;
  }
}

/* ---------------------------------------------------------------------------
   Second factor: the enrollment QR and the sheet of recovery codes.
   ------------------------------------------------------------------------- */

.totp-qr {
  margin: 12px 0 4px;
}

.recovery-codes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 24px;
  margin: 0 0 24px;
  padding: 0;
  list-style: none;
  font-size: 16px;
}

/* ---------------------------------------------------------------------------
   Declarations: V4's generator «Declaraciones» page
   (src/app/dashboard/generador/declaraciones/page.tsx) -- the amber draft
   pill in the header, the amber "you download it, you file it" notice, and
   the card's eyebrow. The pending list reuses the inventory table.
   ------------------------------------------------------------------------- */

.draft-badge {
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 600;
  color: #b45309;
  white-space: nowrap;
  background: #fef3c7;
  border: 1px solid #fde68a;
  border-radius: 9999px;
}

.declaration-notice {
  margin: 0 0 24px;
  padding: 14px 18px;
  font-size: 13px;
  line-height: 1.5;
  color: #92400e;
  background: #fffbeb;
  border: 1px solid #fde68a;
  border-radius: 10px;
}

.card-eyebrow {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--placeholder);
}

.pending-reasons {
  margin: 0;
  padding-left: 18px;
  white-space: normal;
}

/* A counter-offer under its row in the list of offers sent: the generator's
   terms, and the sender's two answers to them. Spans the whole row, below
   the six columns. */
.offer-counter {
  grid-column: 1 / -1;
  padding-top: 8px;
}

.offer-counter small {
  display: block;
  margin-bottom: 8px;
}

.offer-counter .inline-form:first-of-type {
  margin-left: 0;
}

/* ---------------------------------------------------------------------------
   The heat map («Mapa de residuos»): the map itself, its legend and the
   swatches the legend and the table below it share. The map's height is
   fixed rather than a share of the viewport so the legend and the table stay
   reachable on a phone without scrolling past a full-screen map; full screen
   is a mode of its own, below. The legend's text is the page's normal size
   rather than `.hint`'s, because it says what a red point does and does not
   mean.
   ------------------------------------------------------------------------- */

/* Every Leaflet map -- this one and «Ubicación»'s `.location-map` -- is its
   own stacking context. Leaflet gives its panes z-index 400 to 700 and its
   controls 1000, and without isolation those numbers compete with the rest
   of the page: the open header menu (`.app-menu-panel`, z-index 10) was
   painted under the map, and its links under the zoom buttons took the taps
   meant for them (measured at 390x844 on «Ubicación», 2026-10-04).
   Isolated, the whole map paints as one layer at the page's own level, under
   anything the page positions above it, while Leaflet's layers keep their
   order inside it.

   Rejected: raising the menu's z-index above 1000. It would have to outbid
   numbers Leaflet chooses, and any version or plugin that raises them again
   brings the bug back, and it would have to stay below full screen's 2000
   (`.heat-stage.is-full-screen`, below) or the header would sit over a
   full-screen map. Isolation does not disturb full screen: the map is
   inside the fixed block whose 2000 lifts it, map and all, over the header.

   The selector is Leaflet's own class rather than this file's map classes,
   so a map added on another page is isolated without anyone remembering
   to; before the script runs there is no class and nothing to isolate. */
.leaflet-container {
  isolation: isolate;
}

.heat-map {
  height: 420px;
  margin-bottom: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.heat-legend p {
  margin: 8px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text);
}

.heat-scale {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin: 0;
  padding: 0;
  font-size: 13px;
  list-style: none;
}

.heat-swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-right: 6px;
  vertical-align: -1px;
  border: 1px solid rgba(0, 0, 0, 0.35);
  border-radius: 50%;
}

/* The table under the map: each figure's breakdown on its own line, and the
   long folios allowed to wrap at their hyphens instead of widening the table. */
.heat-points small {
  display: block;
}

.heat-points a,
.heat-points [data-need] {
  overflow-wrap: anywhere;
}

.heat-popup p {
  margin: 0 0 4px;
}

/* The filters and the layer switches, which sit with the map in one block
   (`.heat-stage`) so full screen keeps them at hand. */
.heat-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 24px;
  align-items: flex-start;
  margin-bottom: 12px;
  padding: 12px 16px;
  font-size: 13px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.heat-filters fieldset {
  margin: 0;
  padding: 0;
  border: 0;
}

.heat-filters legend,
.heat-region {
  font-weight: 600;
  color: var(--text-strong);
}

.heat-filters fieldset label {
  display: block;
  font-weight: 400;
}

.heat-region select {
  display: block;
  margin-top: 2px;
  padding: 6px 8px;
  font-weight: 400;
}

.heat-filter-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-self: center;
}

.heat-filter-note {
  flex-basis: 100%;
  margin: 0;
}

/* Carriers and plants: a lettered marker rather than a coloured one, so the
   two layers are told apart from the waste points, and from each other,
   without telling colours apart. */
.heat-place {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-right: 6px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: #ffffff;
  border: 1px solid #1f2937;
  border-radius: 3px;
}

.heat-place--carrier {
  background: #1d4ed8;
}

.heat-place--plant {
  background: #6d28d9;
}

.heat-places {
  margin-top: 8px;
}

/* Full screen: the block of filters and map laid over the whole window. The
   same rule serves the browser's own full screen and the fallback without it
   (or without JavaScript), so both look alike. Above Leaflet's panes and
   controls, whose z-index reaches 1000 -- isolated inside the map now (see
   `.leaflet-container`), so 2000 is what lifts the block over the header,
   its open menu included. */
.heat-stage.is-full-screen {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  padding: 12px;
  overflow: auto;
  background: var(--surface-muted);
}

.heat-stage.is-full-screen .heat-map {
  flex: 1 1 auto;
  min-height: 320px;
  height: auto;
  margin-bottom: 0;
}

body:has(.heat-stage.is-full-screen) {
  overflow: hidden;
}

.heat-popup ul {
  margin: 0 0 4px;
  padding-left: 16px;
}

/* ---------------------------------------------------------------------------
   «Ubicación»: the small map where a branch places its point. Small on
   purpose -- it locates one yard, not a region -- and fixed in height so the
   fields under it stay in reach on a phone.
   ------------------------------------------------------------------------- */

.location-map {
  height: 280px;
  margin: 18px 0 8px;
  background: var(--surface-muted);
  border: 1px solid var(--border);
  border-radius: 12px;
}

/* The private-mode page: what the mode hides and what it does not, as two
   short lists, so neither reads as a footnote of the other. */
.private-mode-scope {
  margin: 8px 0 0;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text);
}

/* A generator's operational notes, on its own page and on the sistema de
   gestión's network screens: free text typed by the company, so a long word
   or a pasted URL wraps instead of widening the page past a phone's screen. */
.operational-notes {
  font-size: 14px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

/* The printable ficha (templates/traceability/movement_print.html): the
   bar above the document, and the route as a numbered list of legs, each
   leg's facts one per line. A refused leg is marked as the ficha's notice
   is, in red, so the refusal stands out on paper as it does on screen. */
.rm-print-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  max-width: 720px;
  margin: 0 auto 16px;
}

.rm-route ol {
  margin: 0;
  padding-left: 22px;
  font-size: 13px;
}

.rm-route li {
  margin-bottom: 12px;
}

.rm-route li > strong,
.rm-route li > span {
  display: block;
}

.rm-route-rejection {
  color: #7f1d1d;
}

/* ---------------------------------------------------------------------------
   Printing. The monthly bulletin (templates/reporting/bulletin.html) is
   printed by the browser -- its «Imprimir» is how it becomes a PDF, since no
   PDF is generated on the server -- so a printed page drops what only works
   on screen: the app's header with its navigation, and anything marked
   ``no-print`` (the month selector, the download link). Cards lose their
   shadow and padding so the tables use the paper's width, and a scrolling
   table is printed whole rather than clipped. Written for every page rather
   than for the bulletin's alone: no printed page needs the navigation. The
   movement's printable ficha (templates/traceability/movement_print.html)
   is printed the same way.
   ------------------------------------------------------------------------- */

@media print {
  body {
    background: #ffffff;
  }

  .app-header,
  .no-print {
    display: none !important;
  }

  .card {
    box-shadow: none;
    padding: 0;
    break-inside: avoid;
  }

  .card + .card {
    margin-top: 24px;
  }

  .table-scroll {
    overflow: visible;
  }

  .bulletin-table tr {
    break-inside: avoid;
  }

  /* The printable ficha uses the paper's width, and no leg of its route or
     line of its CDF is split across two pages. */
  .rm-print {
    max-width: none;
    padding: 0;
    box-shadow: none;
  }

  .rm-route li,
  .rm-line {
    break-inside: avoid;
  }
}
