:root {
  --ink: #1c2321;
  --ink-soft: #5b6b66;
  --paper: #f6f4ef;
  --card: #ffffff;
  --line: #dfd9ca;
  --accent: #2f5d50;
  --accent-soft: #e4efe9;
  --warn: #b3541e;
  --warn-soft: #f6e6d9;
  --danger: #a6303c;
  --danger-soft: #f6dfe0;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
  --topbar-h: 52px;
  --toolbar-h: 52px;
}
* { box-sizing: border-box; }
/* Belt-and-suspenders: this exact bug (an author-stylesheet `display`
   silently beating the browser's own `[hidden] { display: none }` UA rule,
   since origin outranks specificity) has already been hit and fixed more
   than once in this app's own stylesheets (most recently
   assessment.css's .as-scope-row-main[hidden]) — this global rule closes the
   whole bug class so it can't keep recurring one stylesheet at a time.
   `!important` is deliberate here: `[hidden]` is meant to always win. */
[hidden] { display: none !important; }
/* Visually hidden but still in the accessibility tree (unlike `display:none`
   or a bare `title` attribute on a non-interactive element, neither of which
   screen readers reliably expose) — for text that has a compact visible
   stand-in (e.g. "4m") but needs its full sentence announced. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* Flex column body instead of calc(100dvh - fixed-header-height) math: the
   header, an optional flash message, and main all become flex children, so
   main.layout-shell always fills exactly whatever space is actually left —
   correct whether or not a flash message is showing, with no coordination
   needed between this file and whatever conditionally renders one.

   height (not min-height): a column flex container needs a DEFINITE height
   for "flex: 1 1 auto" children to actually receive a bounded share of it —
   min-height only sets a floor, so when content is taller than the
   viewport the container's height just grows to fit that content instead,
   and every "fill remaining space" child down the tree falls back to its
   own natural size. That's what let a long work-order list and its
   sibling map balloon to the content's full height instead of the
   intended scrollable panes — confirmed empirically (a 111-row list
   rendered at ~16500px instead of being clipped to the viewport). A fixed
   height still allows normal page scrolling on layout-form pages when
   content overflows — this only fixes what the flex algorithm sees as
   "available space to distribute," not overflow/scroll behavior itself. */
body {
  margin: 0;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  font-family: -apple-system, "Segoe UI", Inter, sans-serif;
  background: var(--paper);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
header {
  flex: 0 0 auto;
  min-height: var(--topbar-h);
  /* env() falls back to the 24px on either side when the device has no
     notch/safe-area (env() with no matching inset is simply 0, so the
     calc() below degrades to plain 24px) — one rule for both cases. */
  padding: 0 calc(24px + env(safe-area-inset-right)) 0 calc(24px + env(safe-area-inset-left));
  border-bottom: 1px solid var(--line);
  background: var(--card);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  box-shadow: 0 1px 2px rgba(28, 35, 33, 0.03);
}
header h1 { margin: 0; font-size: 1.05rem; letter-spacing: -0.01em; }
/* The one-tab tab strip used to be its own full-width row below the header
   (an extra ~40px of chrome for a single, un-navigable "Work Orders" label)
   — folded in here as a small muted section label next to the app name
   instead. Becomes a real nav element again the day a second tab exists.
   Still a real link (see layouts/app.blade.php): deleting the tab strip
   removed the ONLY global way back to /dashboard from anywhere else in the
   app (the admin users page and the AppSuite-connect flow have no back-link
   of their own), which is a real dead-end on an installed PWA with no
   browser chrome to fall back on. */
.topbar-left { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.topbar-section { font-size: 0.8rem; font-weight: 600; color: var(--ink-soft); white-space: nowrap; text-decoration: none; }
.topbar-section:hover { color: var(--accent); text-decoration: underline; }
header a { color: var(--accent); text-decoration: none; font-size: 0.85rem; font-weight: 600; }
header a:hover { text-decoration: underline; }

/* Default layout: centered form-width pages (login, connect flow) — grows
   to at least fill the viewport but the page scrolls normally if taller. */
main.layout-form { flex: 1 0 auto; padding: 28px; max-width: 900px; margin: 0 auto; width: 100%; }
/* Full-bleed layout: the dashboard. Consumes exactly whatever's left of the
   viewport (flex:1 1 auto + min-height:0, not a calc() against the header's
   height) and is itself a flex column so its own children (toolbar, an
   optional banner, the list+map grid) share that space correctly without
   any "does a banner exist" class toggling. */
main.layout-shell {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0;
  /* The dashboard is full-bleed (list+map fill the whole width), so it's
     the one screen where a landscape notch could actually clip content —
     the centered layout-form pages never get wide enough to reach it. */
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
  /* Applied HERE, not as padding on #db-map itself: an inset on #db-map
     doesn't work — Leaflet's `.leaflet-bottom` control positions against the
     PADDING box (which includes padding, so it still lands at the true
     bottom edge) and Leaflet sizes itself from `clientHeight` (also
     padding-inclusive, so no space is actually reclaimed). Shrinking this
     flex ancestor's content-box width instead genuinely reduces how wide
     #db-map ends up, which is what actually keeps the OSM attribution text
     clear of a landscape notch/home-indicator area. */
  padding-bottom: env(safe-area-inset-bottom);
  max-width: none;
  display: flex;
  flex-direction: column;
  /* Single-scroll dashboard layout (round 3): this flex ancestor is now the
     one scroll container — map (~45vh, in normal flow), toolbar, and list
     all scroll together as one page instead of the list owning its own
     nested `overflow-y: auto` sub-container. Still a bounded flex item
     (flex:1 1 auto + min-height:0 above), so this only ever scrolls the
     content that doesn't fit below the fixed header, never the whole
     document. */
  overflow-y: auto;
}

.card { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 24px; margin-bottom: 16px; }
label { display: block; font-size: 0.8rem; font-weight: 600; margin-bottom: 4px; color: var(--ink-soft); }
input[type=text], input[type=email], input[type=password], textarea, select {
  width: 100%; font: inherit; padding: 9px 11px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--paper); color: var(--ink);
}
select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%), linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%); background-position: calc(100% - 18px) center, calc(100% - 13px) center; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 32px; }
textarea { min-height: 90px; font-family: var(--mono); font-size: 0.85rem; }
button { font: inherit; font-weight: 600; padding: 9px 16px; border: none; border-radius: 8px; background: var(--accent); color: white; cursor: pointer; }
button:hover { opacity: 0.92; }
button:focus-visible, a:focus-visible, select:focus-visible, input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Without this, two quick taps close together (e.g. rapid-tapping a
   quantity stepper, or a chip toggle) can be interpreted by iOS Safari as
   a double-tap-to-zoom gesture, silently eating the second tap. Scoped to
   actual interactive elements/classes across the app rather than disabling
   pinch-zoom at the viewport level, which would remove a real accessibility
   affordance for no benefit. assessment.css's .as-stepper-btn already had
   this rule locally (Milestone 4, before this app-wide one existed) — left
   in place as harmless redundancy rather than removed. */
button, a, input, select,
.db-chip, .db-act, .db-clear, .db-vanished-cancel,
.as-chip, .as-transition-btn, .as-si-remove, .as-stepper-btn {
  touch-action: manipulation;
}
.status { flex: 0 0 auto; background: var(--accent-soft); color: var(--accent); padding: 10px 14px; border-radius: 8px; margin: 12px 24px 0; font-size: 0.9rem; }
.errors { flex: 0 0 auto; background: var(--danger-soft); color: var(--danger); padding: 10px 14px; border-radius: 8px; margin: 12px 24px 0; font-size: 0.9rem; }
main.layout-form .status, main.layout-form .errors { margin: 0 0 16px; }
.muted { color: var(--ink-soft); font-size: 0.85rem; }
.wo-number { font-family: var(--mono); font-weight: 600; }

/* Priority / lifecycle-status pill. Was defined only in dashboard.css, but
   the assessment page's header (assessment/edit.blade.php) uses the same
   class for its priority and status badges and does NOT load dashboard.css
   — so there they rendered as bare, full-size spans: a 1.3rem "High" block
   in the corner and a plain-text "New" occupying a row of its own. The base
   rule lives here now, unchanged; the dashboard-only `.db-badge.unknown`
   variant stays in dashboard.css. Kept deliberately light/secondary — the
   label TEXT is never removed (colorblind/glare conditions genuinely can't
   rely on a color alone), only its visual weight is turned down. */
.db-badge {
  display: inline-flex; align-items: center; gap: 3px; font-size: 0.64rem; font-weight: 600;
  padding: 1px 6px; border-radius: 999px; white-space: nowrap; flex-shrink: 0;
}

/* A plain-text-looking button/link that still needs a real 44px tap target
   — the admin users page's Save/Reset/Deactivate/Reactivate row actions were
   `padding: 0` inline styles before this pass (a confirmed ~20px hit area). */
.link-btn {
  background: none; border: none; padding: 12px 6px; margin: -12px -6px;
  font: inherit; font-size: 0.85rem; font-weight: 600; color: var(--accent); cursor: pointer;
}
.link-btn.danger { color: var(--danger); }

/* App-wide touch-target floor (Apple's documented 44px minimum, already this
   app's own convention on several individual controls — see .db-act,
   .as-stepper-btn, .as-si-remove). A single rule here instead of hunting
   down every small `<button>`/`<select>`/text input one at a time: the
   dashboard's filter chips, search box, and sort select, plus the admin
   page's role selects and (via .link-btn above) its row-action buttons,
   were all confirmed under 30px.
   A handful of elements set their own larger/smaller explicit min-height
   and win on specificity as intended (.db-act, .as-stepper-btn, .as-chip).
   The original version of this rule only listed text/email/password inputs
   — missed input[type=number] (the scope-item quantity field), datetime-
   local (the assessment page's contact-date field), and a few types not
   currently used but worth covering defensively (tel/search) plus textarea
   (already ≥44px via its own min-height:90px rule, listed here anyway so
   this selector is the single source of truth for "every real text-entry
   control", not an incomplete one two people have to remember to extend). */
button,
input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], input[type=datetime-local], input[type=tel], input[type=search],
textarea, select {
  min-height: 44px;
}

/* Small avatar/menu control replacing the header's previous four separate
   inline items (Manage users / + Connect an account / name / Log out) —
   the name-only text read as a disabled nav item with no visible role, and
   the four items wrapped to two lines at phone width. */
.user-menu { position: relative; flex: 0 0 auto; }
/* 44px, matching the app-wide touch-target floor below — this was the one
   interactive control in the new header that didn't get it (was 40px). */
.avatar-btn {
  width: 44px; height: 44px; min-height: 44px; border-radius: 999px; padding: 0;
  background: var(--accent); color: #fff; font-weight: 700; font-size: 0.95rem;
  display: flex; align-items: center; justify-content: center; border: none; cursor: pointer;
}
.user-menu-panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; min-width: 220px;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 8px 24px rgba(28, 35, 33, 0.14); padding: 6px; display: flex; flex-direction: column;
}
.user-menu-identity {
  padding: 10px 12px; font-size: 0.82rem; color: var(--ink-soft); border-bottom: 1px solid var(--line); margin-bottom: 4px;
}
.user-menu-identity strong { color: var(--ink); font-weight: 700; }
/* The logout button is nested in its own <form> inside this panel — style
   only the actual interactive element (a/button), not the form wrapper too,
   or "Log out" doubles up the block/padding treatment and renders visibly
   taller/differently-indented than the two sibling plain links. */
.user-menu-panel a, .user-menu-panel button {
  display: block; width: 100%; text-align: left; padding: 10px 12px; border-radius: 6px;
  font-size: 0.85rem; font-weight: 600; color: var(--ink); text-decoration: none;
  background: none; border: none; cursor: pointer; min-height: 44px;
}
.user-menu-panel form { display: contents; }
.user-menu-panel a:hover, .user-menu-panel button:hover { background: var(--paper); text-decoration: none; }
