:root {
  --bg: #f5f6f8;
  --surface: #ffffff;
  --border: #e2e5ea;
  --text: #1c2430;
  --muted: #5b6675;
  --primary: #14603a;
  --primary-ink: #ffffff;
  --danger: #b3261e;
  --accent: #2f6f4f;
  --radius: 8px;
  --shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.1);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  background: var(--bg);
  line-height: 1.5;
}

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

.layout { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }

.sidebar {
  background: #10261b;
  color: #d7e3db;
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.sidebar .brand { font-weight: 700; color: #fff; font-size: 1.05rem; margin-bottom: 16px; }
.sidebar a {
  color: #cfe0d6;
  text-decoration: none;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 0.92rem;
}
.sidebar a:hover, .sidebar a.active { background: rgba(255,255,255,0.09); color: #fff; }
.sidebar .group-label { color: #7fa08e; font-size: 0.72rem; text-transform: uppercase; letter-spacing: .05em; margin: 14px 10px 4px; }

/* Shell sections of the sidebar. On desktop they stack; the media query at the
   bottom of this file turns them into a top bar. */
.sidebar-nav { display: flex; flex-direction: column; gap: 4px; }
.sidebar-foot { margin-top: auto; display: flex; flex-direction: column; gap: 8px; }
.sidebar-foot .email { font-size: .78rem; padding: 0 10px; word-break: break-all; }
.sidebar-head { display: flex; align-items: center; gap: 10px; }
/* Only exists once the nav collapses; see the tablet breakpoint below. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: 6px;
  background: transparent;
  color: #cfe0d6;
  cursor: pointer;
}
.nav-toggle:hover { background: rgba(255,255,255,0.09); color: #fff; }
.nav-toggle svg { fill: currentColor; display: block; }

/* min-width:0 overrides the grid item's automatic minimum size, which by
   default is its content's min-content — a native <input type="file"> has an
   intrinsic width that doesn't shrink on its own, and without this it was
   forcing the shared 1fr track (so the sidebar too) wider than the viewport
   on any page a file field renders on. */
.main { display: flex; flex-direction: column; min-width: 0; }
.topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 12px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.topbar .user { color: var(--muted); font-size: 0.88rem; }
.content { padding: 24px; max-width: 1100px; width: 100%; }

h1 { font-size: 1.4rem; margin: 0 0 16px; }
h2 { font-size: 1.1rem; margin: 24px 0 12px; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px;
  margin-bottom: 20px;
}

table { width: 100%; border-collapse: collapse; background: var(--surface); }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
th { color: var(--muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: .03em; }
tr:last-child td { border-bottom: none; }

.btn {
  display: inline-block;
  background: var(--primary);
  color: var(--primary-ink);
  border: none;
  border-radius: 6px;
  padding: 9px 16px;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { background: #0f4d2f; }
.btn.secondary { background: transparent; color: var(--accent); border: 1px solid var(--border); }
.btn.danger { background: var(--danger); }
.btn.small { padding: 5px 10px; font-size: 0.8rem; }

.actions { display: flex; gap: 8px; align-items: center; }

form .field { margin-bottom: 14px; display: flex; flex-direction: column; gap: 4px; }
label { font-size: 0.85rem; font-weight: 600; color: var(--muted); }
input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], input[type=url], select, textarea {
  width: 100%;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.92rem;
  font-family: inherit;
  background: #fff;
}
textarea { min-height: 90px; }
/* Read-only value shown in place of an input (e.g. the invited email). */
.static-value {
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.92rem;
  background: var(--bg, #f6f7f9);
  color: var(--muted);
}
.hint { font-size: 0.78rem; color: var(--muted); }

/* Custom file trigger (§30): a native <input type="file"> renders its button
   and "no file chosen" text as one intrinsic, unstyleable unit that can't
   wrap — long filenames get clipped or force the layout wider (see .main
   min-width:0 above). The real input stays functional and keyboard-reachable
   (clip technique, not display:none) but visually hidden; a real <button>
   triggers it, and the filename renders in our own element that can wrap. */
.file-picker { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.file-picker-input {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.file-name { font-size: 0.88rem; color: var(--muted); word-break: break-word; }

/* photo-crop dialog (§30) */
.crop-dialog {
  width: min(640px, 92vw);
  padding: 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.crop-dialog::backdrop { background: rgba(16, 24, 40, 0.55); }
.crop-dialog-hint { margin: 0 0 12px; font-size: 0.85rem; color: var(--muted); }
.crop-dialog-stage {
  border-radius: 6px;
  overflow: hidden;
  background: #10261b;
}
/* Cropper.js hides the original <img> and inserts <cropper-canvas> as its
   sibling; without an explicit size it only takes its min-height (100px), so
   the image (auto-fit "contain") renders tiny. Standardize the canvas to the
   same 4:3 the crop outputs, so the working area is a consistent, real size
   regardless of the source photo's own dimensions. */
.crop-dialog-stage cropper-canvas {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  max-height: 70vh;
}
.crop-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }

.flash { padding: 12px 16px; border-radius: 6px; margin-bottom: 16px; font-size: 0.9rem; }
.flash.notice { background: #e7f4ec; color: #14603a; border: 1px solid #bfe3cd; }
.flash.alert { background: #fdeceb; color: #8f1d16; border: 1px solid #f3c6c2; }

.badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 0.74rem; font-weight: 600; background: #eef1f4; color: var(--muted); }
.badge.published { background: #e7f4ec; color: #14603a; }
.badge.draft { background: #eef1f4; color: #5b6675; }
.badge.in_review { background: #fff4e2; color: #8a5a00; }
.badge.suspended, .badge.expired, .badge.closed,
.badge.rejected, .badge.revoked { background: #fdeceb; color: #8f1d16; }
.badge.expiring_soon, .badge.in_process, .badge.pending { background: #fff4e2; color: #8a5a00; }
.badge.valid, .badge.approved { background: #e7f4ec; color: #14603a; }
.badge.verified, .badge.verified_plus { background: #e7f4ec; color: #14603a; }
.badge.basic { background: #eef4ff; color: #23407a; }
.badge.unverified { background: #eef1f4; color: #5b6675; }

.errors { background: #fdeceb; border: 1px solid #f3c6c2; color: #8f1d16; padding: 12px 16px; border-radius: 6px; margin-bottom: 16px; }
.errors ul { margin: 6px 0 0; padding-left: 18px; }

/* Photo preview for the gallery table and the upload form — staff need to
   see the actual image, not just its alt text, to moderate it. */
.thumb { width: 44px; height: 44px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border); display: block; }
.thumb.lg { width: 140px; height: 140px; border-radius: 8px; margin-bottom: 8px; }

.empty { color: var(--muted); padding: 24px; text-align: center; }
.muted { color: var(--muted); }
.right { text-align: right; }

/* Auth screen */
.auth-wrap { min-height: 100vh; display: grid; place-items: center; background: var(--bg); }
.auth-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); padding: 32px; width: 360px; }
.auth-card h1 { text-align: center; }
.auth-card .brand { text-align: center; color: var(--primary); font-weight: 700; margin-bottom: 4px; }
.auth-card .btn { width: 100%; text-align: center; margin-top: 8px; }

/* Dashboard counters that lead to their queue. The hover is the affordance:
   without it a clickable card is indistinguishable from a static stat. */
.card-link {
  display: block;
  text-decoration: none;
  color: inherit;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.card-link:hover {
  border-color: var(--accent);
  box-shadow: 0 2px 6px rgba(16, 24, 40, .1);
}

/* Paginated listing footer. */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  font-size: 0.85rem;
}

/* Column grids used by the forms and the read-only ficha. They were inline
   styles, which no media query can override — that is why they live here. */
.grid { display: grid; gap: 16px; }
.grid.two { grid-template-columns: 1fr 1fr; }
.grid.three { grid-template-columns: 1fr 1fr 1fr; }
.grid.wide-narrow { grid-template-columns: 2fr 1fr; }
.grid.tight { gap: 8px 24px; }
/* A field that would look absurd stretched across the card (a year, a count). */
.narrow { max-width: 200px; }

/* Title (or section heading) on one side, its buttons on the other. */
.page-head, .card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.page-head { margin-bottom: 16px; }
.page-head h1 { margin: 0 0 4px; }
.card-head h2 { margin: 0; }
/* Unlike .page-head, this never had its own bottom margin — the title/button
   row butted straight into the table below it (worst on table.stack, where
   the first row is its own bordered card sitting right under the button). */
.card-head { margin-bottom: 16px; }
/* A card-head sitting directly inside a `.card` with padding:0 (a table-only
   card) needs its own padding — the card gave it up so the table can bleed
   edge to edge. */
.card-head.pad { padding: 14px 16px 0; }

/* Dashboard summary tiles. A bare grid of numbers doesn't say which one to
   read first; .attention marks the ones tied to a queue that needs action. */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  margin-bottom: 8px;
}
.stat-label { color: var(--muted); font-size: .8rem; }
.stat-value { font-size: 1.8rem; font-weight: 700; line-height: 1.2; }
.stat.attention { border-left: 3px solid #d99a1f; }
.stat.attention .stat-value { color: #8a5a00; }

/* Buttons that close a form. Separated from .actions because on a phone these
   go full width and the destructive one is pushed away from the rest. */
.form-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

.list { list-style: none; margin: 0; padding: 0; }
.list li { border-bottom: 1px solid var(--border); }
.list li:last-child { border-bottom: none; }
.list-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  min-height: 52px;
  color: inherit;
  text-decoration: none;
  font-weight: 600;
}
.list-link:hover { background: #f7f9f8; }

/* Checkbox rows (fishing modes, techniques, cover photo). */
.check-row { font-weight: 400; display: flex; gap: 8px; align-items: center; }
.check-list { display: flex; flex-wrap: wrap; gap: 12px; }

/* Operation picker (portal landing). Each card has to answer "which one is
   this and does it need me?" without opening it. */
.op-list {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
/* A lone operation — the common case — should not stretch across the desk. */
.op-list > .op-card:only-child { max-width: 520px; }

.op-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
  color: inherit;
  text-decoration: none;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.op-card:hover { border-color: var(--accent); box-shadow: 0 2px 6px rgba(16, 24, 40, .1); }
.op-card-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.op-name { font-size: 1.05rem; font-weight: 700; }
.op-chevron { color: var(--muted); font-size: 1.4rem; line-height: 1; }
.op-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.op-types { margin: 8px 0 0; font-size: .85rem; }

.op-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 14px 0 0;
  padding: 12px 0 0;
  border-top: 1px solid var(--border);
}
/* Reversed so the number reads first while the markup keeps dt before dd. */
.op-stats > div { display: flex; flex-direction: column-reverse; }
.op-stats dt { font-size: .7rem; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); font-weight: 600; }
.op-stats dd { margin: 0; font-size: 1.2rem; font-weight: 700; line-height: 1.2; }

.op-alerts { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 0; padding: 0; }
.op-alerts li { background: #fff4e2; color: #8a5a00; border-radius: 999px; padding: 4px 10px; font-size: .76rem; font-weight: 600; }
.op-ok { margin: 12px 0 0; font-size: .78rem; color: var(--muted); }
.subtitle { margin: 0; font-size: .9rem; }

/* ---------------------------------------------------------------------------
   Phones and tablets: the nav collapses behind a hamburger. This breaks earlier
   (1024px) than the content rules below, because a 240px sidebar stops being
   affordable long before a two-column form does.
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  /* `grid-template-rows` is not optional: the grid keeps min-height 100vh, so
     an auto row stretches to soak up the leftover space and the bar grows into
     a green slab on any short page. */
  .layout { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .sidebar {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    /* Same reason, one level down: wrapped flex lines spread apart unless the
       lines are packed to the start. */
    align-content: flex-start;
    padding: 8px 12px;
    gap: 6px;
    /* One short row now, so it can stay put: the menu is reachable without
       scrolling back to the top. */
    position: sticky;
    top: 0;
    z-index: 20;
  }
  .sidebar .brand { margin-bottom: 0; font-size: 0.95rem; }
  .nav-toggle { display: flex; }

  /* The bar keeps only the toggle and the brand; links *and* the account block
     live inside the menu. Both collapse until the toggle puts .nav-open on the
     sidebar, and both take a full row so opening it never squeezes the brand. */
  .sidebar-head { order: 0; flex: 1 1 auto; min-width: 0; }
  .sidebar-nav { order: 1; flex: 1 0 100%; display: none; }
  .sidebar-foot { order: 2; flex: 1 0 100%; display: none; margin-top: 0; }
  .sidebar .btn.small { min-height: 40px; }

  .sidebar.nav-open .sidebar-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 4px;
    padding-top: 8px;
    border-top: 1px solid rgba(255,255,255,0.14);
    /* The staff menu is long enough to outgrow a landscape phone; the account
       block below has to stay reachable. */
    max-height: calc(100vh - 190px);
    overflow-y: auto;
  }
  .sidebar-nav a { min-height: 44px; display: flex; align-items: center; }
  .sidebar .group-label { margin: 10px 10px 2px; }

  /* Closing the menu is the last thing in it, under the account it belongs to:
     the topbar shows a name, this says which login that is. */
  .sidebar.nav-open .sidebar-foot {
    display: flex;
    gap: 8px;
    padding-top: 8px;
    margin-top: 4px;
    border-top: 1px solid rgba(255,255,255,0.14);
  }
  .sidebar-foot .email { padding: 0 10px 2px; }
  .sidebar-foot form { width: 100%; }
  .sidebar-foot .btn { width: 100%; text-align: center; }
}

/* ---------------------------------------------------------------------------
   Phones. Most operators administer their operation from one, so this is the
   primary layout for /operator, not a fallback.
   --------------------------------------------------------------------------- */
@media (max-width: 768px) {
  .topbar { padding: 10px 16px; gap: 12px; }
  .topbar strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .topbar .user { font-size: 0.8rem; text-align: right; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .content { padding: 16px 12px 32px; }

  .card { padding: 14px; margin-bottom: 14px; }
  h1 { font-size: 1.25rem; }

  /* One column: two fields side by side on a 360 px screen leave no room to
     type in either. */
  .grid.two, .grid.three, .grid.wide-narrow { grid-template-columns: 1fr; gap: 12px; }
  .op-list { grid-template-columns: 1fr; gap: 12px; }
  .op-list > .op-card:only-child { max-width: none; }
  .op-card { padding: 14px; }
  .narrow { max-width: none; }

  /* Thumb-sized targets. 44 px is the smallest reliably tappable box. */
  .btn { padding: 12px 16px; min-height: 44px; line-height: 1.2; }
  .btn.small { padding: 9px 12px; min-height: 38px; font-size: 0.85rem; }
  input[type=checkbox], input[type=radio] { width: 20px; height: 20px; }
  .check-list { gap: 10px 16px; }

  /* 16 px keeps iOS Safari from zooming the page when a field takes focus. */
  input[type=text], input[type=email], input[type=password], input[type=number],
  input[type=date], input[type=url], input[type=file], select, textarea {
    font-size: 16px;
    padding: 11px 12px;
  }
  textarea { min-height: 110px; }

  /* Form buttons stack full width; the destructive one keeps its distance. */
  .form-actions { flex-direction: column; align-items: stretch; }
  .form-actions > *, .form-actions form { width: 100%; }
  .form-actions .btn { width: 100%; text-align: center; }
  .form-actions form:has(.btn.danger) { margin-top: 12px; }

  /* photo-crop dialog: the operator portal is mobile-first, so this is the
     primary rendering, not a fallback. A full-width dialog with more vertical
     room for the canvas and thumb-sized, stacked action buttons. */
  /* Chromium's UA stylesheet caps a modal <dialog> below 100vw on its own
     (measured ~38px short); an explicit max-width overrides that default. */
  .crop-dialog { width: 100vw; max-width: 100%; padding: 16px; border-radius: 0; }
  .crop-dialog-stage cropper-canvas { aspect-ratio: 4 / 3; max-height: 55vh; }
  .crop-dialog-actions { flex-direction: column-reverse; align-items: stretch; }
  .crop-dialog-actions .btn { width: 100%; text-align: center; }

  .page-head, .card-head { align-items: flex-start; }
  /* width:100% alone isn't enough once a page-head carries 3+ buttons (e.g.
     Publicar/Editar/Invitar dueño/Eliminar on the staff operator ficha):
     flex:1 with no wrap forces them all onto one row and the row overflows
     the viewport. wrap + a min flex-basis lets extras drop to their own row
     instead of pushing the page wide. */
  .page-head .actions, .card-head .actions { width: 100%; flex-wrap: wrap; }
  .page-head .actions > .btn, .page-head .actions form { flex: 1 1 140px; }
  .page-head .actions .btn { width: 100%; text-align: center; }

  /* Row-per-record tables become a stack of labelled cards. Reading a 5-column
     table sideways on a phone is the worst part of the desktop layout. */
  table.stack, table.stack tbody, table.stack tr, table.stack td { display: block; width: 100%; }
  table.stack thead { display: none; }
  table.stack tr {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px 12px;
    margin-bottom: 10px;
  }
  table.stack tr:last-child { margin-bottom: 0; }
  table.stack td { border: none; padding: 4px 0; display: flex; gap: 12px; justify-content: space-between; align-items: center; }
  table.stack td[data-label]::before {
    content: attr(data-label);
    color: var(--muted);
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .03em;
    flex: 0 0 auto;
  }
  /* The action button needs to read as its own zone, not another data row —
     a divider plus real spacing (mirroring .op-stats) instead of the bare
     10px the data rows use for their own top padding. */
  table.stack td.right { text-align: left; margin-top: 8px; padding-top: 12px; border-top: 1px solid var(--border); }
  table.stack td.right .btn { width: 100%; text-align: center; }

  /* Any table not converted still has to stay inside the viewport. Overriding
     display to `block` here was the bug: a <table>'s row/cell children keep
     their table-part display values regardless, so the browser wraps them in
     an anonymous table box to preserve table layout — and that anonymous
     table ignores the `width: 100%` above (that rule now targets the outer
     block box, not the box actually doing column layout) and shrinks to its
     content width instead, leaving the card mostly empty. Leaving `table`
     alone keeps it a single real table box, so it still fills the card and
     only scrolls sideways where the content genuinely needs it. */
  table:not(.stack) { overflow-x: auto; white-space: nowrap; }

  .pagination { flex-direction: column; align-items: stretch; gap: 8px; text-align: center; }
  .pagination .actions { justify-content: center; }

  .auth-card { width: 100%; max-width: 400px; border-radius: 0; border-left: none; border-right: none; padding: 24px 20px; }
  .auth-wrap { align-items: start; padding: 24px 0; }
}

/* Drag-and-drop gallery ordering (#64). */
.drag-cell { width: 34px; }
.drag-handle {
  display: inline-block; padding: 4px 6px; border-radius: 6px;
  color: var(--muted); font-size: 1rem; line-height: 1;
  cursor: grab; user-select: none; touch-action: none;
}
.drag-handle:active { cursor: grabbing; }
.drag-handle:hover { background: #eef1f4; }
.drag-handle.disabled { cursor: default; opacity: 0.35; }
.drag-handle.disabled:hover { background: none; }
/* Taken-down media sits at the bottom and cannot be dragged; dim it so the
   list reads as two blocks rather than one with dead rows in it. */
tr[data-gallery-sort-fixed] { opacity: 0.6; }
.sortable-ghost { opacity: 0.35; }
.sortable-chosen { background: #f6f8fa; }
