/* ==========================================================================
   graco.f19.org — TECH FIELD APP stylesheet (styles the src/Views/tech views)
   --------------------------------------------------------------------------
   LOCATION IS LOAD-BEARING: this file must live at graco/assets/tech.css and
   deploy to /public_html/graco.f19.org/assets/tech.css. graco.f19.org's
   document root IS the tech app's own folder, so shell.php's <link href=
   "/assets/tech.css"> resolves here. It previously sat in the Rocket app's
   public/assets/ — a DIFFERENT docroot on a different host name, where this
   host cannot reach it and every screen renders as unstyled HTML. Do not
   move it back.
   --------------------------------------------------------------------------
   Deliberately NOT app.css. The desktop dashboard optimises for information
   density on a 1400px screen; this optimises for one thumb, direct sunlight,
   and gloves on a roof. It is therefore a standalone sheet with its own tokens
   so a change here can never regress the dashboard (and vice versa).

   Hard rules baked in:
     - system font stack only. No Google Fonts, no CDN, no build step — a tech
       on 2 bars of LTE must not wait on a font server.
     - 17px base, and EVERY form control >= 17px: iOS Safari zooms the viewport
       when a focused input is under 16px, which throws the layout sideways and
       is unrecoverable one-handed.
     - >= 44px tap targets (Apple HIG minimum); primary actions get 56-64px
       because the real target is a gloved thumb, not a cursor.
     - contrast pushed well past WCAG AA. Mid-greys that read fine indoors are
       invisible on a phone at noon, so "muted" text here is still ~7:1.
     - safe-area insets on all four sides — notch, home indicator, and the
       landscape ears on a rotated iPhone.
     - dark mode via prefers-color-scheme (garage / attic / evening).

   Colour is never the ONLY signal: every status pill and confidence chip also
   carries a word, because sunlight washes hue out long before it kills text.
   ========================================================================== */

:root{
  --bg:#EEF1F4;
  --card:#FFFFFF;
  --card-2:#F6F8FA;
  --ink:#0C1116;          /* near-black: max contrast in sun */
  --muted:#41505C;        /* still ~8:1 on --card, NOT a light grey */
  --line:#C3CCD4;
  --line-strong:#98A5B0;

  --accent:#1F7A69;       /* primary action — Graco deep teal (AA on white) */
  --accent-ink:#FFFFFF;
  --accent-soft:#E2F1ED;
  --brand-1:#3C9C8E;      /* Graco logo gradient: teal -> green -> lime */
  --brand-2:#4EC585;
  --brand-3:#CCED54;

  --ok:#0B6B33;
  --ok-soft:#DCF3E4;
  --warn:#8A4B00;         /* amber, darkened so it survives sunlight */
  --warn-soft:#FDECD2;
  --danger:#A31219;
  --danger-soft:#FCE4E5;
  --neutral:#3A4650;
  --neutral-soft:#E6EAEE;

  --shadow:0 1px 2px rgba(12,17,22,.10), 0 4px 14px rgba(12,17,22,.07);
  --r:14px;
  --r-sm:10px;

  --pad:14px;
  --tap:52px;             /* standard control height */
  --tap-lg:64px;          /* primary action height */
}

@media (prefers-color-scheme: dark){
  :root{
    --bg:#0A0E12;
    --card:#161C22;
    --card-2:#1E252C;
    --ink:#F2F5F8;
    --muted:#B4C0CB;
    --line:#333E48;
    --line-strong:#4C5A66;

    --accent:#4EC585;     /* brand green reads better than teal on dark */
    --accent-ink:#06211A;
    --accent-soft:#123027;

    --ok:#5FD08A;
    --ok-soft:#12301E;
    --warn:#F5BE6B;
    --warn-soft:#3A2A0E;
    --danger:#FF8189;
    --danger-soft:#3A1417;
    --neutral:#C4CED8;
    --neutral-soft:#242C34;

    --shadow:0 1px 2px rgba(0,0,0,.5), 0 6px 18px rgba(0,0,0,.4);
  }
}

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

html{
  -webkit-text-size-adjust:100%;          /* rotating must not resize text */
  background:var(--bg);
}

body{
  font:400 17px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
       "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  color:var(--ink);
  background:var(--bg);
  min-height:100%;
  overflow-x:hidden;                       /* never sideways-scroll the page */
  -webkit-tap-highlight-color:rgba(11,79,203,.15);
}

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

/* ── header ───────────────────────────────────────────────────────────────
   Sticky so "who am I / what day / get out" is always one thumb away, and
   short so it never eats the job list. */
.t-head{
  position:sticky; top:0; z-index:50;
  display:flex; align-items:center; gap:10px;
  background:var(--card);
  border-bottom:1px solid var(--line);
  padding:10px calc(var(--pad) + env(safe-area-inset-right))
          10px calc(var(--pad) + env(safe-area-inset-left));
  padding-top:calc(10px + env(safe-area-inset-top));
  box-shadow:var(--shadow);
}
.t-head .t-back{
  display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px; margin-left:-10px;
  border-radius:50%; text-decoration:none;
  color:var(--ink); font-size:1.6rem; line-height:1; flex:0 0 auto;
}
.t-head .t-back:active{ background:var(--card-2); }
.t-head .t-id{ flex:1 1 auto; min-width:0; }
.t-head .t-id b{ display:block; font-size:1.05rem; font-weight:700;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.t-head .t-id span{ display:block; font-size:.82rem; color:var(--muted); }
.t-head form{ margin:0; flex:0 0 auto; }
.t-out{
  min-height:44px; padding:0 14px;
  border:1px solid var(--line-strong); border-radius:22px;
  background:var(--card); color:var(--ink);
  font:600 .88rem/1 inherit; font-family:inherit; cursor:pointer;
}
.t-out:active{ background:var(--card-2); }

/* Leads sits beside Sign out: the people working leads are the same people who
   run the job list, so it is one tap from anywhere. The badge is written by the
   lead board's poller and stays hidden on pages that never poll. */
.t-leads{
  position:relative; text-decoration:none; color:inherit; font-weight:700;
  font-size:.86rem; padding:8px 11px; border-radius:9px;
  background:var(--card-2); margin-right:8px; white-space:nowrap;
}
.t-leads:active{ opacity:.75; }
.t-badge{
  position:absolute; top:-6px; right:-6px; min-width:18px; height:18px; padding:0 5px;
  border-radius:999px; background:#e5484d; color:#fff; font-size:.68rem; line-height:18px;
  text-align:center; font-weight:800; box-shadow:0 0 0 2px var(--bg,#161C22);
}

/* ── page body ───────────────────────────────────────────────────────────── */
/* Pages with no .t-head (the login screen) put .t-main hard against the top of
   the display. On a notched phone in standalone mode that slid the Graco mark
   under the status bar. .t-head pays this inset for every other page. */
.t-nohead .t-main{ padding-top:calc(var(--pad) + env(safe-area-inset-top)); }

.t-main{
  max-width:640px; margin:0 auto;
  padding:var(--pad) calc(var(--pad) + env(safe-area-inset-right))
          calc(34px + env(safe-area-inset-bottom))
          calc(var(--pad) + env(safe-area-inset-left));
}
.t-main > * + *{ margin-top:14px; }

.t-h1{ font-size:1.45rem; font-weight:800; letter-spacing:-.01em; }
.t-h2{ font-size:.82rem; font-weight:800; letter-spacing:.06em;
       text-transform:uppercase; color:var(--muted); }
.t-sub{ font-size:.95rem; color:var(--muted); }

.t-card{
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--r); box-shadow:var(--shadow);
  padding:16px;
}
.t-card.tight{ padding:0; overflow:hidden; }

/* ── messages ─────────────────────────────────────────────────────────────
   Wordy on purpose. "Error" alone is useless to someone standing in a yard. */
.t-msg{ border-radius:var(--r-sm); padding:13px 15px; font-size:.98rem;
        border:1px solid transparent; }
.t-msg.err{ background:var(--danger-soft); border-color:var(--danger); color:var(--danger); font-weight:600; }
.t-msg.ok { background:var(--ok-soft);     border-color:var(--ok);     color:var(--ok);     font-weight:600; }
.t-msg.warn{ background:var(--warn-soft);  border-color:var(--warn);   color:var(--warn);   font-weight:600; }
.t-msg.info{ background:var(--card-2);     border-color:var(--line);   color:var(--ink); }

/* ── buttons ──────────────────────────────────────────────────────────────
   Full-width by default: on a phone, a centred 120px button is a dart throw. */
.t-btn{
  display:flex; align-items:center; justify-content:center; gap:9px;
  width:100%; min-height:var(--tap); padding:12px 16px;
  border:1px solid var(--line-strong); border-radius:var(--r-sm);
  background:var(--card); color:var(--ink);
  font:600 1.02rem/1.2 inherit; font-family:inherit;
  text-decoration:none; text-align:center; cursor:pointer;
  -webkit-appearance:none; appearance:none;
}
.t-btn:active{ transform:translateY(1px); background:var(--card-2); }
.t-btn.primary{
  min-height:var(--tap-lg);
  background:var(--accent); border-color:var(--accent); color:var(--accent-ink);
  font-size:1.12rem; font-weight:700;
}
.t-btn.primary:active{ filter:brightness(.92); background:var(--accent); }
.t-btn.danger{ color:var(--danger); border-color:var(--danger); }
.t-btn[disabled], .t-btn[aria-disabled="true"]{ opacity:.55; }
.t-btn-row{ display:flex; gap:10px; }
.t-btn-row .t-btn{ flex:1 1 0; }

/* ── forms ───────────────────────────────────────────────────────────────── */
.t-label{ display:block; font-size:.88rem; font-weight:700; color:var(--muted);
          margin-bottom:6px; }
.t-input{
  display:block; width:100%; min-height:var(--tap);
  padding:12px 14px;
  border:1.5px solid var(--line-strong); border-radius:var(--r-sm);
  background:var(--card); color:var(--ink);
  font:400 17px/1.3 inherit; font-family:inherit;   /* 17px: no iOS zoom */
  -webkit-appearance:none; appearance:none;
}
.t-input:focus{ outline:3px solid var(--accent); outline-offset:1px; border-color:var(--accent); }
.t-field + .t-field{ margin-top:14px; }
/* Serial/model: monospace so 0/O and 1/I/l are distinguishable at a glance. */
.t-input.code{
  font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size:17px; letter-spacing:.04em; font-weight:600;
}

/* ── PIN ─────────────────────────────────────────────────────────────────── */
.t-pin{
  width:100%; max-width:280px; margin:0 auto;
  text-align:center; letter-spacing:.55em; text-indent:.55em;   /* indent re-centres the tracked text */
  font-size:2rem; font-weight:700; min-height:var(--tap-lg);
  font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ── roster picker (login) ───────────────────────────────────────────────── */
.t-pick{ list-style:none; }
.t-pick li + li{ border-top:1px solid var(--line); }
.t-pick label{
  display:flex; align-items:center; gap:14px;
  min-height:60px; padding:12px 16px; cursor:pointer;
}
.t-pick input{ position:absolute; opacity:0; pointer-events:none; }
.t-pick .dot{
  flex:0 0 auto; width:26px; height:26px; border-radius:50%;
  border:2.5px solid var(--line-strong); background:var(--card);
}
.t-pick .nm{ font-size:1.08rem; font-weight:600; }
.t-pick input:checked + .dot{
  border-color:var(--accent); background:var(--accent);
  box-shadow:inset 0 0 0 5px var(--card);
}
.t-pick input:checked ~ .nm{ font-weight:800; }
.t-pick label:has(input:checked){ background:var(--accent-soft); }
.t-pick input:focus-visible + .dot{ outline:3px solid var(--accent); outline-offset:2px; }

/* ── date strip (jobs) ───────────────────────────────────────────────────── */
.t-dates{ display:flex; gap:8px; }
.t-dates a{
  flex:1 1 0; display:flex; flex-direction:column; align-items:center; justify-content:center;
  min-height:56px; padding:6px 4px;
  border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--card); color:var(--ink); text-decoration:none;
}
.t-dates a b{ font-size:.98rem; font-weight:700; }
.t-dates a span{ font-size:.76rem; color:var(--muted); }
.t-dates a[aria-current="page"]{
  background:var(--accent); border-color:var(--accent); color:var(--accent-ink);
}
.t-dates a[aria-current="page"] span{ color:var(--accent-ink); opacity:.85; }

/* ── job cards ───────────────────────────────────────────────────────────── */
.t-jobs{ display:flex; flex-direction:column; gap:12px; }
.t-job{
  display:block; text-decoration:none; color:inherit;
  background:var(--card); border:1px solid var(--line);
  border-left:6px solid var(--line-strong);
  border-radius:var(--r); box-shadow:var(--shadow);
  padding:14px 16px; min-height:88px;
}
.t-job:active{ background:var(--card-2); }
.t-job.s-dispatched{ border-left-color:var(--ok); }
.t-job.s-completed { border-left-color:var(--neutral); }
.t-job.s-canceled  { border-left-color:var(--danger); }
.t-job.s-scheduled, .t-job.s-confirmed{ border-left-color:var(--accent); }
.t-job .when{ font-size:1.18rem; font-weight:800; letter-spacing:-.01em; }
.t-job .who { font-size:1.06rem; font-weight:600; margin-top:2px; }
.t-job .where{ font-size:.95rem; color:var(--muted); margin-top:2px; }
.t-job .meta{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-top:10px; }

/* ── pills / chips ───────────────────────────────────────────────────────── */
.t-pill{
  display:inline-flex; align-items:center; gap:5px;
  padding:4px 11px; border-radius:999px;
  font-size:.78rem; font-weight:800; letter-spacing:.03em; text-transform:uppercase;
  background:var(--neutral-soft); color:var(--neutral);
  border:1px solid transparent;
}
.t-pill.ok    { background:var(--ok-soft);     color:var(--ok); }
.t-pill.warn  { background:var(--warn-soft);   color:var(--warn); }
.t-pill.danger{ background:var(--danger-soft); color:var(--danger); }
.t-pill.info  { background:var(--accent-soft); color:var(--accent); }
.t-pill.hollow{ background:transparent; border-color:var(--line-strong); color:var(--muted); }

/* ── key/value rows (job + customer detail) ──────────────────────────────── */
.t-kv{ display:flex; flex-direction:column; }
.t-kv > div{ display:flex; gap:12px; padding:11px 0; border-top:1px solid var(--line); }
.t-kv > div:first-child{ border-top:0; padding-top:0; }
.t-kv dt, .t-kv .k{ flex:0 0 108px; font-size:.86rem; font-weight:700; color:var(--muted); }
.t-kv dd, .t-kv .v{ flex:1 1 auto; min-width:0; word-break:break-word; }

/* Big tap-to-act rows: call, navigate, open customer. */
.t-act{
  display:flex; align-items:center; gap:13px;
  min-height:var(--tap-lg); padding:12px 15px;
  border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--card); color:inherit; text-decoration:none;
}
.t-act + .t-act{ margin-top:10px; }
.t-act:active{ background:var(--card-2); }
.t-act .ic{ flex:0 0 auto; font-size:1.45rem; line-height:1; }
.t-act .tx{ flex:1 1 auto; min-width:0; }
.t-act .tx b{ display:block; font-size:1.04rem; font-weight:700; }
.t-act .tx span{ display:block; font-size:.85rem; color:var(--muted); }
.t-act .go{ flex:0 0 auto; color:var(--muted); font-size:1.2rem; }

/* ── list rows (captures, job history) ───────────────────────────────────── */
.t-list{ list-style:none; }
.t-list > li{ padding:13px 16px; border-top:1px solid var(--line); }
.t-list > li:first-child{ border-top:0; }
.t-list .ttl{ font-weight:700; }
.t-list .sub{ font-size:.86rem; color:var(--muted); margin-top:2px; }
.t-list .rt{ float:right; font-weight:700; }

/* ── capture screen ──────────────────────────────────────────────────────── */
.t-shot{ display:flex; gap:14px; align-items:flex-start; }
.t-shot img{
  flex:0 0 auto; width:118px; height:118px; object-fit:cover;
  border-radius:var(--r-sm); border:1px solid var(--line); background:var(--card-2);
}
.t-shot .side{ flex:1 1 auto; min-width:0; }

/* A field whose AI confidence is low/none must NOT look like an answer. Amber
   frame + tinted well + a "CHECK THIS" chip; the value is a starting point. */
.t-field.flag .t-input{ border-color:var(--warn); border-width:2.5px; background:var(--warn-soft); }
.t-field.flag .t-label{ color:var(--warn); }
.t-conf{ float:right; font-size:.72rem; font-weight:800; letter-spacing:.04em;
         text-transform:uppercase; padding:2px 8px; border-radius:999px; }
.t-conf.high{ background:var(--ok-soft); color:var(--ok); }
.t-conf.medium{ background:var(--accent-soft); color:var(--accent); }
.t-conf.low, .t-conf.none{ background:var(--warn-soft); color:var(--warn); }
.t-note{ font-size:.92rem; color:var(--muted); margin-top:6px; }

/* ── camera / upload ─────────────────────────────────────────────────────── */
/* The <input type=file> is hidden and driven by its <label>, so the tech taps a
   64px button instead of the browser's native (tiny, unstyleable) control. */
.t-cam{ position:relative; }
.t-cam input[type="file"]{
  position:absolute; width:1px; height:1px; opacity:0; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap;
}
.t-cam input[type="file"]:focus-visible + .t-btn{ outline:3px solid var(--accent); outline-offset:2px; }

/* Full-screen "we're working" cover. A 10MB photo over LTE plus a vision call
   is 10-20 seconds of apparently-frozen screen; without this the tech taps
   again and uploads twice. */
.t-busy{
  position:fixed; inset:0; z-index:200;
  display:none; flex-direction:column; align-items:center; justify-content:center;
  gap:18px; padding:32px;
  background:var(--bg); color:var(--ink); text-align:center;
}
.t-busy[data-on="1"]{ display:flex; }
.t-busy .spin{
  width:54px; height:54px; border-radius:50%;
  border:5px solid var(--line); border-top-color:var(--accent);
  animation:t-spin 1s linear infinite;
}
.t-busy b{ font-size:1.18rem; }
.t-busy span{ font-size:.98rem; color:var(--muted); max-width:22rem; }
@keyframes t-spin{ to{ transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce){
  .t-busy .spin{ animation-duration:2.4s; }
  .t-btn:active{ transform:none; }
}

/* ── empty state ─────────────────────────────────────────────────────────── */
.t-empty{ text-align:center; padding:40px 20px; }
.t-empty .big{ font-size:2.6rem; line-height:1; }
.t-empty p{ margin-top:12px; font-size:1.05rem; color:var(--muted); }

/* ── install banner ("add to home screen") ────────────────────────────────
   A bottom SHEET, deliberately not a modal: nothing behind it is blocked, no
   focus trap, no scrim. It sits ABOVE the home indicator (safe-area-inset-
   bottom) — without that inset iOS puts the swipe bar straight through the
   buttons. It also cannot be allowed to sit on top of the job screen's "Take
   photo of data plate", so `body.t-install-on` below buys the page enough
   bottom padding to scroll clear of it.

   Hidden by default and revealed by shell.php's script, so JS-off never sees
   it; the explicit [hidden] rule is because these elements carry a `display`
   and a declared display beats the UA's [hidden]{display:none}. */
.t-install{
  position:fixed; left:0; right:0; bottom:0; z-index:150;
  background:var(--card);
  border-top:1px solid var(--line);
  /* shadow points UP — this element's edge is its top one */
  box-shadow:0 -2px 6px rgba(12,17,22,.10), 0 -10px 28px rgba(12,17,22,.12);
  padding:15px calc(var(--pad) + env(safe-area-inset-right))
          calc(15px + env(safe-area-inset-bottom))
          calc(var(--pad) + env(safe-area-inset-left));
  animation:t-install-up .22s ease-out both;
}
.t-install[hidden]{ display:none; }
/* The Graco gradient as a 3px cap: brand signal that costs no vertical space
   and no contrast, since nothing is written on it. */
.t-install::before{
  content:""; position:absolute; left:0; right:0; top:0; height:3px;
  background:linear-gradient(90deg, var(--brand-1), var(--brand-2), var(--brand-3));
}
@keyframes t-install-up{ from{ transform:translateY(102%); } to{ transform:none; } }

.t-install-in{
  position:relative;                       /* the close button's anchor */
  display:flex; flex-wrap:wrap; align-items:center; gap:12px;
  max-width:640px; margin:0 auto;          /* same measure as .t-main */
}
.t-install .ic{ flex:0 0 auto; width:42px; height:42px; border-radius:9px; }
/* padding-right keeps the copy out from under the close button, which is
   absolutely positioned and therefore takes no layout space. */
.t-install .tx{ flex:1 1 11rem; min-width:0; padding-right:44px; }
.t-install .tx b{ display:block; font-size:1.06rem; font-weight:800; letter-spacing:-.01em; }
.t-install .tx span{ display:block; margin-top:3px; font-size:1rem; color:var(--muted); }
.t-install .tx span[hidden]{ display:none; }
/* The iOS Share glyph, drawn inline so the instruction names the icon the tech
   is actually looking at rather than the word "share". */
.t-install .share{
  width:1.05em; height:1.05em; vertical-align:-.16em;
  fill:none; stroke:currentColor; stroke-width:1.9;
  stroke-linecap:round; stroke-linejoin:round;
}
/* Full width on its own line: this is the one-tap install on Android, and a
   .t-btn.primary is already 64px tall — a gloved-thumb target, not a link. */
.t-install-go{ flex:1 0 100%; }
.t-install-go[hidden]{ display:none; }
.t-install-x{
  position:absolute; top:-6px; right:-6px;
  display:flex; align-items:center; justify-content:center;
  width:44px; height:44px;                 /* HIG minimum, even for a dismiss */
  border:0; border-radius:50%;
  background:transparent; color:var(--muted);
  font:400 1.3rem/1 inherit; font-family:inherit; cursor:pointer;
  -webkit-appearance:none; appearance:none;
}
.t-install-x:active{ background:var(--card-2); }
.t-install-x:focus-visible{ outline:3px solid var(--accent); outline-offset:2px; }

/* The reserve is whatever the sheet actually measures — shell.php writes its
   offsetHeight into --t-install-h on show. The 178px fallback only applies for
   the one frame before that lands; the Android banner is really ~206px on a
   375px screen, and under-reserving parked it on top of the page's last button
   (on the capture screen, "Save to ServiceTitan"). */
body.t-install-on .t-main{
  padding-bottom:calc(var(--t-install-h, 178px) + 12px + env(safe-area-inset-bottom));
}

@media (prefers-color-scheme: dark){
  .t-install{ box-shadow:0 -2px 6px rgba(0,0,0,.55), 0 -12px 30px rgba(0,0,0,.5); }
}
@media (prefers-reduced-motion: reduce){
  .t-install{ animation:none; }
}

.t-foot{ margin-top:22px; text-align:center; font-size:.82rem; color:var(--muted); }
