/* ==========================================================================
   DZT FIELD APP — the private portal for crews, foremen and the owner.

   Same design system as dztlandscaping.com: every colour, face and type step
   here comes from `tokens.css`, which is GENERATED from the marketing
   stylesheet (tools/app-tokens.mjs). Nothing in this file invents a colour.
   The two surfaces are separate Netlify origins for security reasons and must
   still read as one company.

   BUT IT IS NOT THE MARKETING SITE, and copying its layout would be the wrong
   kind of consistency. The marketing site is read on a couch, at leisure, to
   decide whether to trust a contractor. This is read ON A PHONE, IN A YARD,
   IN JULY SUN, BY SOMEONE WEARING GLOVES, one-handed, often with no signal.
   Every departure below is that difference, and only that difference:

     · Tap targets are 56px minimum, 64px for the one button that matters.
       A gloved fingertip is roughly 14mm; the marketing site's 40px buttons
       are a miss waiting to happen when the miss costs a payroll entry.
     · Type does not go below --step-0, and --faint is used for NOTHING
       load-bearing. In direct sun a phone loses most of its usable contrast
       range, and 4.9:1 in a dark office is not 4.9:1 on a jobsite.
     · No hover-only affordance anywhere. There is no cursor here.
     · State is always said in words, never only in colour — a screen read in
       sun through polarised safety glasses can lose a hue entirely.
   ========================================================================== */

/* ---- 1. Shell ---------------------------------------------------------- */

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

/* THIS RULE IS LOAD-BEARING AND WAS FOUND BY LOOKING, NOT BY REASONING.
   `hidden` is how every screen in this app is switched — signed-in vs signed-
   out, the empty state, the outbox lines, the access and gate notes. It works
   through the user-agent rule `[hidden] { display: none }`, which carries the
   specificity of a single attribute selector. The moment a rule below says
   `#signin { display: grid }`, an ID beats it and the hidden element renders.

   The first screenshot of this stylesheet showed the sign-in form AND the job
   list on screen together, which is a signed-out surface displaying a signed-
   in one. `!important` is correct here rather than lazy: nothing should ever
   out-rank an explicit instruction not to display something. */
[hidden] { display: none !important; }

html {
  /* Prevent iOS bumping text up a size in landscape, which reflows a card
     mid-tap. */
  -webkit-text-size-adjust: 100%;
}

/* ---- 0. DARK. ---------------------------------------------------------
   OWNER DECISION 2026-08-08: the portal is dark, matching the slate the logo
   sits on. Not a new palette — `--slate-deep` and `--slate` are the marketing
   site's own inverted-band colours and `--bone` is the type that already runs
   on them, so this is the same design system with the bands turned up rather
   than a second theme to keep in step.

   It also happens to be right for the job. This screen is read at 6am in a
   truck and at 2pm in full sun; a white page at 6am is a flashbulb, and the
   dark ground keeps the one bright thing on screen — the button you are
   reaching for — the thing your eye lands on.

   The surfaces are three steps apart so a card reads as raised without a
   shadow, which is what actually survives a phone at half brightness. */
:root {
  --app-bg:      var(--slate-deep);       /* the page */
  --app-surface: #1A1E19;                 /* a card, one step up from the page */
  --app-raised:  #262B25;                 /* an inset inside a card */
  --app-text:    var(--bone);
  --app-mute:    var(--bone-mute);
  --app-line:    var(--bone-line);
  --app-accent:  var(--volt-hi);          /* 6.9:1 on the plate; --volt is 5.3 */
}

body {
  margin: 0;
  background: var(--app-bg);
  color: var(--app-text);
  font-family: var(--body);
  font-size: var(--step-0);
  line-height: 1.5;
  /* viewport-fit=cover is set in the markup, so the notch and the home
     indicator are ours to avoid. A Clock-in button under the home bar is a
     button that gets missed. */
  padding:
    max(1rem, env(safe-area-inset-top))
    max(1rem, env(safe-area-inset-right))
    max(1.5rem, env(safe-area-inset-bottom))
    max(1rem, env(safe-area-inset-left));
  -webkit-font-smoothing: antialiased;
}

#app { max-width: 34rem; margin: 0 auto; }

h1 {
  font-family: var(--display);
  font-size: var(--step-2);
  font-weight: 700;
  letter-spacing: -0.015em;
  margin: 0 0 0.25rem;
}

/* ---- 2. Status line ---------------------------------------------------
   The first thing on screen and the most important: it says whether this
   phone is online, working from cache, or signed out. It is a full band
   rather than a sentence because it must survive being glanced at. */

#status {
  margin: 0 0 1.25rem;
  padding: 0.85rem 1rem;
  border-radius: var(--r);
  background: var(--slate);
  color: var(--bone);
  font-weight: 600;
  font-size: var(--step-0);
}

#status[hidden] { display: none; }

/* ---- 3. Sign-in ---------------------------------------------------------

   THE ONE SCREEN THAT IS NOT A WORK SCREEN.

   Everything past section 5 is laid out for speed with gloves on. This is
   different: it is the only surface a person meets before they trust the
   system, and a stack of inputs on a flat colour says "internal tool nobody
   maintains". So it is composed — a photograph of work this company actually
   built on one side, the form on the other, over a ground with depth.

   PHOTOGRAPHY IS THE ARGUMENT, which is the marketing site's own thesis, and
   the portal is the same company. On a phone the panel is gone entirely: a
   crew signing in at 6am in a truck wants the field and nothing else, and a
   hero image above the fold on a small screen is a scroll before you can type.

   It borrows the site's own material rather than inventing a look: Archivo for
   display, the same green, the same button treatment. Inverted, because this
   screen opens at 6am and a white page then is a flashbulb.
   -------------------------------------------------------------------------- */

/* The ground: two very wide washes over the slate so the background falls away
   instead of reading as one flat fill. Fixed, so it does not travel with the
   card on a short screen. */
body:has(#signin:not([hidden])) {
  background:
    radial-gradient(70rem 40rem at 8% -12%, rgba(14, 122, 84, 0.20), transparent 60%),
    radial-gradient(58rem 34rem at 96% 6%, rgba(38, 43, 37, 0.55), transparent 62%),
    var(--app-bg);
  background-attachment: fixed;
}

#signin {
  display: grid;
  gap: 1.25rem;
  justify-items: center;
  align-content: start;
  padding: min(9vh, 4rem) 0 2rem;
}

/* ---- the photograph ---- */
.auth-aside { display: none; }

.auth-card {
  width: min(26rem, 100%);
  padding: 1.9rem 1.7rem 1.6rem;
  border-radius: var(--r-lg);
  background: linear-gradient(168deg, rgba(30, 36, 30, 0.97), rgba(17, 21, 17, 0.97));
  border: 1px solid rgba(243, 245, 244, 0.11);
  /* The hairline across the top is what makes a dark card read as lit rather
     than painted — the same trick --glass-lift plays on the marketing site,
     inverted for a dark ground. */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.11),
    0 1px 2px rgba(0, 0, 0, 0.30),
    0 18px 40px -20px rgba(0, 0, 0, 0.65),
    0 46px 90px -55px rgba(0, 0, 0, 0.85);
}

/* ---- SPLIT, from the width where a form beside a picture stops being a
        compromise and starts being a composition. ---- */
@media (min-width: 62rem) {
  /* THE SPLIT NEEDS THE WHOLE VIEWPORT, and #app is capped at 34rem for the
     work screens — which is right for them and fatal here: the first attempt
     rendered a full-bleed composition inside a 545px column, and the headline
     was clipped mid-word. The cap and the body padding are lifted for this
     screen only, by :has() on the signed-out state, so the work screens keep
     their measure. */
  body:has(#signin:not([hidden])) { padding: 0; }
  body:has(#signin:not([hidden])) #app { max-width: none; margin: 0; }

  #signin {
    justify-items: stretch;
    padding: 0;
    min-height: 100vh;
    min-height: 100dvh;
    /* stretch, not center: with content-sized rows the photograph stopped
       short of the top and bottom and the composition read as a band floating
       on a dark page. The rows fill the viewport and the CARD is what centres,
       inside its own area. */
    align-content: stretch;
    grid-template-columns: 1.05fr 0.95fr;
    grid-template-rows: 1fr auto;
    grid-template-areas: 'aside card' 'aside foot';
    align-items: stretch;
    gap: 0;
  }

  .auth-aside {
    grid-area: aside;
    display: block;
    position: relative;
    align-self: stretch;
    overflow: hidden;
    border-right: 1px solid rgba(243, 245, 244, 0.10);
  }

  .auth-aside img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Slightly desaturated and darkened so the photograph is a GROUND, not a
       competitor. The form is what the person came here to use. */
    filter: saturate(0.85) contrast(1.02);
  }

  .auth-aside__scrim {
    position: absolute;
    inset: 0;
    background:
      linear-gradient(90deg, rgba(27, 34, 41, 0.30) 0%, rgba(17, 21, 17, 0.88) 100%),
      linear-gradient(0deg, rgba(27, 34, 41, 0.70) 0%, transparent 55%);
  }

  .auth-aside__line {
    position: absolute;
    left: clamp(2rem, 5vw, 4.5rem);
    right: clamp(2rem, 5vw, 4.5rem);
    bottom: clamp(2.5rem, 7vh, 5rem);
    margin: 0;
  }

  .auth-aside__line p {
    margin: 0;
    font-family: var(--display);
    font-size: var(--step-3);
    font-weight: 800;
    line-height: 1.02;
    letter-spacing: -0.025em;
    color: #FFFFFF;
    text-wrap: balance;
  }

  .auth-aside__line cite {
    display: block;
    margin-top: 0.9rem;
    font-style: normal;
    font-size: var(--step--1);
    font-weight: 650;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--sage);
  }

  .auth-card {
    grid-area: card;
    align-self: center;
    width: min(25rem, 100%);
    margin: 0 auto;
    padding-inline: clamp(1rem, 3vw, 2rem);
    /* On the split there is no ground behind the card to lift off, so it drops
       its border and shadow and becomes the right-hand PANEL. A floating card
       inside a panel is one bevel too many. */
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0;
  }

  .auth-foot { grid-area: foot; text-align: center; padding-bottom: 2rem; }
}

/* ---- the card's contents ---- */
.auth-head {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1.6rem;
}

/* The real mark, on the plate the marketing header gives it. The logo has a
   transparent ground and fine lettering; without a plate it dissolves. */
.auth-logo {
  width: 56px; height: 56px;
  border-radius: 50%;
  flex: none;
  background: rgba(0, 0, 0, 0.30);
  border: 1px solid rgba(243, 245, 244, 0.12);
  padding: 4px;
  object-fit: contain;
}

.signin-title {
  margin: 0;
  font-size: var(--step-1);
  letter-spacing: -0.012em;
}

.signin-sub {
  margin: 0.15rem 0 0;
  color: var(--app-mute);
  font-size: var(--step--1);
  font-weight: 600;
}

#signin-form { display: grid; gap: 1rem; }
.field { display: grid; gap: 0.4rem; }

.field label {
  font-weight: 650;
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--app-mute);
}

/* Inputs sit DARKER than what is behind them. A well is what a field should
   read as; a pale raised box on a dark ground reads as a button. */
#signin input[type="email"],
#signin input[type="password"],
#signin input[type="text"] {
  min-height: 52px;
  padding: 0.7rem 0.9rem;
  border-radius: 10px;
  border: 1px solid rgba(243, 245, 244, 0.15);
  background: rgba(0, 0, 0, 0.24);
  color: var(--app-text);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.35);
  transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}

#signin input::placeholder { color: rgba(243, 245, 244, 0.32); }

/* A ring rather than an outline, so it follows the radius. Not :focus-visible
   — on this screen the field you are in is the whole orientation, and a tap
   should show it too. */
#signin input:focus {
  outline: none;
  border-color: rgba(158, 169, 179, 0.80);
  background: rgba(0, 0, 0, 0.32);
  box-shadow:
    inset 0 1px 2px rgba(0, 0, 0, 0.35),
    0 0 0 3px rgba(158, 169, 179, 0.22);
}

/* The running answer to "what account is this, exactly". Sits under the field
   in the muted voice, because it is confirmation rather than instruction — and
   it must not read as an error, since the common case is everything going
   right. `min-height` holds the line whether or not there is text in it, so the
   form does not jump under the thumb as somebody types their username. */
.field-hint {
  margin: 0;
  min-height: 1.3em;
  font-size: var(--step--1);
  color: var(--app-mute);
  font-variant-numeric: tabular-nums;
}
/* Same warm tone as #signin-msg. A username with a character an address cannot
   carry is a typo to fix, not a failure. */
.field-hint--warn { color: #E8A87C; }

/* The recovery form shares the card with the sign-in form and takes its turn,
   so it needs the same grid and its own heading block — it has no logo row of
   its own, because the one in .auth-head above it never leaves. */
#recover-form { display: grid; gap: 1rem; }
.recover-head { display: grid; gap: 0.15rem; }

#forgot { justify-self: start; }

.field-with-affix { position: relative; display: grid; }
.field-with-affix input { padding-right: 5rem; }

.affix {
  position: absolute;
  top: 50%;
  right: 0.45rem;
  transform: translateY(-50%);
  min-height: 40px;
  padding: 0 0.7rem;
  border-radius: 8px;
  border: 1px solid rgba(243, 245, 244, 0.14);
  background: rgba(243, 245, 244, 0.06);
  background-image: none;
  box-shadow: none;
  color: var(--app-mute);
  font-size: var(--step--1);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.affix:active { transform: translateY(-50%); background: rgba(243, 245, 244, 0.12); }

.auth-submit {
  width: 100%;
  min-height: 54px;
  margin-top: 0.25rem;
  font-size: var(--step-0);
  letter-spacing: 0.02em;
}

#signin-msg {
  min-height: 1.4em;
  margin: 0;
  font-weight: 650;
  font-size: var(--step--1);
  /* Warm rather than red. Almost everything this line says is "that did not
     match", which is a retry and not a catastrophe — the same family as the
     warn colour the outbox uses to park a refused entry. */
  color: #E8A87C;
}

#boot-error {
  max-width: 34rem;
  margin: 0 auto 1rem;
  padding: 0.85rem 1rem;
  border-radius: var(--r);
  background: #3A2A20;
  border-left: 5px solid #C4703A;
  color: #F3D8C4;
  font-weight: 650;
}

#signup-switch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 1.4rem 0 0;
  padding-top: 1.15rem;
  border-top: 1px solid rgba(243, 245, 244, 0.10);
}

/* A button that reads as a link. It IS a button — it changes this screen's
   mode rather than navigating — and button chrome would put two equal-weight
   buttons on a sign-in form, which is how people press the wrong one. */
.linkish {
  min-height: 0;
  padding: 0;
  background: none;
  background-image: none;
  box-shadow: none;
  border: 0;
  border-bottom: 1px solid rgba(124, 192, 138, 0.45);
  border-radius: 0;
  color: var(--app-accent);
  font-size: var(--step--1);
  font-weight: 650;
}
.linkish:active { background: none; transform: none; }

.signin-note {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--app-mute);
  border: 1px dashed rgba(243, 245, 244, 0.22);
  border-radius: 100px;
  padding: 0.2rem 0.6rem;
}

.auth-foot {
  margin: 0;
  color: var(--app-mute);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.03em;
}

/* The status band belongs to the work screens. On a composed sign-in surface
   it is noise above the card, and "signed out" is already what the screen
   says. The boot error is NOT suppressed — that one has to be seen. */
body:has(#signin:not([hidden])) #status { display: none !important; }


/* ---- 4. Buttons -------------------------------------------------------- */

button {
  min-height: 56px;
  padding: 0.85rem 1.25rem;
  border: 0;
  border-radius: 8px;
  /* --volt, NOT --forest-soft. The cedar regeneration of tokens.css removed
     the whole --forest family, and a var() naming an undefined property is
     invalid at computed-value time: the declaration is DROPPED, not defaulted.
     So the sign-in button had no background at all — the one control on the
     one screen every employee starts at, and nothing reported it because
     nothing renders CSS in a test. `node tools/app-tokens.mjs --check` did. */
  background: var(--volt);
  /* THE GROUND, NOT WHITE, AND THE PALETTE CHANGE IS WHY. White measured
     5.34:1 on the old dark forest; on this lighter green it is 3.33:1 and
     fails. The plate is 5.83:1 — the same relationship every other filled
     green control on this site now uses. */
  color: var(--slate-deep);
  font-family: var(--body);
  font-size: var(--step-0);
  font-weight: 650;
  cursor: pointer;
}

/* THE SPECULAR TREATMENT BELONGS TO THE SIGN-IN SURFACE, AND ONLY THERE.
   It matches the marketing site's button, which is what makes a DZT button
   look like one, and "keep /admin login/signup page look the same as it is
   now" means it stays exactly where the owner last saw it.

   IT USED TO BE ON THE BARE `button` RULE and then removed again by the
   signed-in block below. That worked, and it was one deleted block away from
   putting the gloss back on a screen the owner rejected it from twice —
   silently, on a day nobody was looking at the field app. Applying it where it
   belongs rather than everywhere-then-undoing-it removes the possibility
   instead of covering it. `tools/ai/design-check.mjs` is what found it. */
body:has(#signin:not([hidden])) button {
  /* NO SPECULAR GRADIENT. A light-to-dark sheen on a primary button is the
     single most dated thing in a UI — it is the 2010 "glossy" convention, and
     it is what made the sign-in screen read as a template. The site's buttons
     are flat: the stand's green, a white label, and nothing on top of it. */
  background-image: none;
  box-shadow: 0 1px 0 rgba(255,255,255,0.22) inset, 0 8px 20px -14px rgba(94,154,106,0.7);
}

/* Active goes DARKER than --volt, and --volt-hi is lighter, so the step is
   spelled out: this is the only place that wants a pressed-down green. */
button:active { background-color: #4E8257; transform: translateY(1px); }
button[disabled] { background: var(--app-raised); color: var(--app-mute); box-shadow: none; }

/* The sign-out button is not a primary action and must not compete with
   Clock in — the two must never be confused at a glance. */
#signout {
  background: transparent;
  background-image: none;
  box-shadow: none;
  color: var(--app-mute);
  border: 1.5px solid var(--app-line);
  font-weight: 600;
  min-height: 48px;
}

/* ---- 5. Freshness -----------------------------------------------------
   "Today" is a claim, and on a phone that has been in a pocket since 6am it
   can be a false one. This is where the app admits how old its data is. */

#freshness {
  margin: 0 0 1.5rem;
  color: var(--app-mute);
  font-size: var(--step--1);
  font-weight: 600;
}

/* ---- 6. Job cards ------------------------------------------------------ */

#jobs { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; }

#jobs > li {
  background: var(--app-surface);
  border: 1px solid var(--app-line);
  border-left: 5px solid var(--app-accent);
  border-radius: var(--r);
  padding: 1.1rem 1.15rem 1.25rem;
  /* No `--sh-sm` here. It is a LIGHT-theme shadow — rgba(27,34,41,0.05) — so on
     a black card it was invisible anyway, and the signed-in block below was
     already setting it to none. Elevation that renders as nothing is still
     elevation in the source, and the next person to read this file would have
     had to work out which of the two won. */
}

.job-title {
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 0.35rem;
}

.job-address {
  margin: 0 0 0.5rem;
  font-weight: 600;
  color: var(--app-text);
}

.job-crew {
  margin: 0 0 0.75rem;
  color: var(--app-mute);
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Access notes and gate codes are the two things someone reads while standing
   at a locked gate with a truck behind them. They are boxed, not italicised,
   because emphasis has to survive sunlight. */
.job-access,
.job-gate {
  margin: 0 0 0.6rem;
  padding: 0.7rem 0.85rem;
  border-radius: 8px;
  background: var(--app-raised);
  border-left: 4px solid var(--slate);
  font-weight: 600;
}

.job-gate { font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }
.job-access[hidden], .job-gate[hidden] { display: none; }

/* ---- 7. The clock form ------------------------------------------------
   The single most important control in the product. Cost code first, then the
   button — the order is mandatory in the schema and it is mandatory here,
   because time booked against a job says the job lost money and time booked
   against a cost code says base prep always runs over, and only the second
   ever improves an estimate. */

.job-time {
  display: grid;
  gap: 0.7rem;
  margin: 1rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--app-line);
}

.job-clock {
  width: 100%;
  min-height: 64px;
  font-size: var(--step-1);
}

.job-time-msg {
  min-height: 1.4em;
  font-weight: 650;
  font-size: var(--step--1);
  color: var(--app-accent);
}

#empty {
  padding: 2rem 1rem;
  text-align: center;
  color: var(--app-mute);
  font-weight: 600;
}

/* ---- 7b. Photographs -------------------------------------------------
   The concealment line is the point of this block, so it is styled as a
   warning band rather than as helper text under a control. Somebody about to
   pour over un-photographed compaction has minutes to act on it, and the
   difference between that and a lost warranty argument two years later is
   whether they noticed. #A8541F is the marketing site's own warn colour, the
   same one the outbox uses to park a refused entry. */

.job-photos {
  margin: 1rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--app-line);
}

.job-concealment {
  margin: 0 0 0.85rem;
  padding: 0.8rem 0.95rem;
  border-radius: 8px;
  background: #3A2A20;
  border-left: 5px solid #C4703A;
  color: #F3D8C4;
  font-weight: 650;
}

/* Cleared is stated, not implied by absence. "Every buried element is
   photographed" is worth reading; a blank space says nothing and looks the
   same as a screen that failed to load the list. */
.job-concealment--clear {
  background: var(--app-raised);
  border-left-color: var(--app-accent);
  color: var(--app-text);
}

.job-photo-form { display: grid; gap: 0.7rem; }

.job-photo-take {
  width: 100%;
  background: transparent;
  background-image: none;
  box-shadow: none;
  color: var(--app-accent);
  border: 2px solid var(--app-accent);
  font-weight: 650;
}

.job-photo-take:active { background-color: var(--app-raised); }

/* Deliberately quieter than Clock in. Both are primary actions on this card
   and they must not be confused at a glance by someone wearing gloves — the
   filled button is the one that costs money to get wrong. */
.job-photo-msg {
  min-height: 1.4em;
  font-weight: 650;
  font-size: var(--step--1);
  color: var(--app-accent);
}

/* ---- 8. The outbox ----------------------------------------------------
   A queue nobody can see is a queue nobody trusts, and a crew that does not
   trust it keeps a paper copy — which is the outcome the whole time-entry
   schema exists to prevent. So the outbox is loud on purpose, and each state
   says what it is in words as well as in colour. */

#outbox,
#outbox-rejected,
#outbox-degraded {
  margin: 1.25rem 0 0;
  padding: 0.9rem 1rem;
  border-radius: var(--r);
  font-weight: 650;
}

#outbox {
  background: var(--app-raised);
  border-left: 5px solid var(--slate);
  color: var(--app-text);
}

/* Rejected is the only genuinely bad state, and it is the one that must never
   be mistaken for "sending". #A8541F is the marketing site's own warn colour
   (.callout--warn), not a new one. */
#outbox-rejected {
  /* The light-mode peach was unreadable once the page went dark. Same warn
     HUE as the marketing site keeps the family; the ground and the type swap. */
  background: #3A2A20;
  border-left: 5px solid #C4703A;
  color: #F3D8C4;
}

#outbox-degraded {
  background: var(--app-raised);
  border-left: 5px solid var(--app-line);
  color: var(--app-text);
}

#outbox[hidden], #outbox-rejected[hidden], #outbox-degraded[hidden] { display: none; }

/* ---- 9. Footer --------------------------------------------------------- */

#signed-in > footer {
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--app-line);
  display: grid;
  gap: 0.85rem;
  justify-items: start;
}

#signed-in > footer p {
  margin: 0;
  color: var(--app-mute);
  font-size: var(--step--1);
  font-weight: 600;
}

#who { color: var(--app-text); }

#role {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: 100px;
  background: var(--app-raised);
  color: var(--app-text);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

noscript {
  display: block;
  padding: 1rem;
  background: var(--app-raised);
  border-radius: var(--r);
  font-weight: 600;
}

/* ---- 10. Motion -------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  button:active { transform: none; }
}

/* The way through to the office dashboard, for the roles that have one. */
#office-link-wrap { margin: 0; }
#office-link {
  color: var(--app-accent);
  font-weight: 650;
  font-size: var(--step--1);
  text-decoration: none;
  border-bottom: 1px solid rgba(124, 192, 138, 0.45);
}

/* ==========================================================================
   WHY NOTHING IS LOADING

   Not an error style. The three states this panel shows are instructions with
   a person attached — apply the migrations, insert a role row, or reconnect —
   and styling them as failures sends somebody to look for a fault instead of
   doing the thing. Quiet card, ordered steps, no red.
   ========================================================================== */
#setup {
  max-width: 40rem;
  margin: 1rem auto;
  padding: 1rem 1.15rem 1.1rem;
  border: 1px solid var(--app-line);
  border-radius: 10px;
  background: var(--app-surface);
}
#setup.is-blocking { border-left: 3px solid var(--app-accent); }
#setup h2 {
  margin: 0 0 0.4rem;
  font-size: var(--step-0);
  font-family: var(--display);
  letter-spacing: -0.005em;
  color: var(--app-text);
}
#setup p { margin: 0 0 0.7rem; font-size: var(--step--1); line-height: 1.6; color: var(--app-mute); }
#setup ol { margin: 0; padding-left: 1.2rem; display: grid; gap: 0.45rem; }
#setup li { font-size: var(--step--1); line-height: 1.55; color: var(--app-mute); }


/* The day itself (0028) — above the clock, because whether the day is
   happening comes before how long it took. Uses the app palette as defined at
   the top of this file; the first draft invented --app-warn, --space-3 and
   --fs--1, none of which exist, so the line rendered unstyled and the buttons
   had no target size at all. */
.job-day {
  margin: 0.9rem 0 0.5rem;
  color: var(--app-mute);
  font-size: var(--step--1);
}
.job-day.is-off {
  color: var(--app-accent);
  font-weight: 650;
}

.job-advance {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  align-items: center;
  margin: 0 0 1rem;
}
/* 56px, not 44. This is a thumb in a work glove on a phone held at arm's
   length in sun, and the clock button next to it is 64. */
.job-advance button {
  min-height: 56px;
  padding: 0 1.1rem;
  border: 1px solid var(--app-line);
  border-radius: var(--r-sm);
  background: var(--app-raised);
  color: var(--app-text);
  font: inherit;
  font-weight: 650;
  font-size: var(--step-0);
  cursor: pointer;
}
.job-advance button[hidden] { display: none; }
.job-advance button:disabled { opacity: 0.55; cursor: default; }

.job-advance-msg {
  min-height: 1.4em;
  font-size: var(--step--1);
  color: var(--app-mute);
}
.job-advance-msg.is-warn { color: var(--app-accent); font-weight: 650; }

/* ==========================================================================
   MATTE BLACK — THE SIGNED-IN FIELD APP

   Owner, 2026-08-09: "dark black glass like the websites glass black header",
   then "glass looks cheap/half assed", then "too glossy". The frosted version
   of this block is gone; what is left is the same instruction read correctly.
   Black ground, hairline edges, no blur, no gradient, no shadow.

   THE SECOND HALF OF THE INSTRUCTION IS WHY EVERY SELECTOR IS GATED:
   "keep /admin login/signup page look the same as it is now". `#signin` and
   `#signed-in` live in the same document and are switched with `hidden`, so a
   rule written against `body` alone would repaint the screen the owner asked
   to leave alone. `body:has(#signed-in:not([hidden]))` is the same mechanism
   the sign-in split already uses, one section along.

   THE PROPERTIES HANG OFF <body>, NOT OFF #signed-in, and that was a bug
   first: `#status` and `#setup` are SIBLINGS of `#signed-in` inside `#app` —
   the status band has to be able to say "signed out" — so `#signed-in #status`
   matched nothing and the band kept the old slate while the cards under it
   went black. One grey rectangle at the top of an otherwise converted screen,
   caught in a screenshot.

   WHAT DOES NOT CHANGE, AND MUST NOT: the 56px targets, the 64px clock button,
   the floor of --step-0 on type, and the rule that state is said in words
   rather than only in colour. Those are what make this screen usable
   one-handed, in a glove, in July sun. Only surfaces move.
   ========================================================================== */

body:has(#signed-in:not([hidden])) { background: #000000; }

/* A card is the ground plus one hairline. The 5px sage edge STAYS — it is how
   a crew member finds the card they are working from at a glance, and it is
   the one thing on this screen that should not become subtler. */
body:has(#signed-in:not([hidden])) :is(#jobs > li, #status, #setup) {
  background: #000000;
  border: 1px solid #2F3336;
  box-shadow: none;
}
body:has(#signed-in:not([hidden])) #jobs > li { border-left: 5px solid var(--app-accent); }

/* Insets sit one step off black rather than on a second grey slab. */
body:has(#signed-in:not([hidden])) :is(.job-access, .job-gate, #outbox, #outbox-degraded, #role) {
  background: #0B0D0E;
  border-left-color: #2F3336;
}

/* The two that must NOT go quiet. A rejected entry and un-photographed
   concealment are the only genuinely bad states here, and they keep a solid
   warn ground — a hairline would make them read as ordinary furniture. */
body:has(#signed-in:not([hidden])) :is(#outbox-rejected, .job-concealment) {
  background: #1A1008;
  border-left-color: #C4703A;
}
body:has(#signed-in:not([hidden])) .job-concealment--clear {
  background: #0B0D0E;
  border-left-color: var(--app-accent);
}

/* Flat, and round. The gradient on a primary button is the same specular idea
   as the glass, one control at a time. */
body:has(#signed-in:not([hidden])) button {
  background-image: none;
  box-shadow: none;
  border-radius: 999px;
}
