/* ==========================================================================
   Crew rostering — the worker app. "The blue work light."

   Design direction, and why it is the opposite of the coordinator console.

   The coordinator console is paper: a ruled, pale, dense prompt book, because it
   replaces a spreadsheet somebody reads all day at a desk. This is not that. This
   is one person, one hand, thirty seconds, in one of two lighting conditions:

     * a loading dock at 6:50am, direct sun on the screen; or
     * a dark auditorium during a show call, where a bright phone is genuinely
       antisocial — crew get told off for screen glow, and they will not open an app
       that lights them up mid-show.

   So the surface is dark, and that is a decision made from the subject rather than
   a fashion: the deep blue-black is the auditorium under blues, and the accent is
   WORKLIGHT AMBER — the colour of the working light that is on when the house is
   dark and the crew are in. Amber is also the one hue that survives both
   conditions: it stays legible against sun glare and does not flare at night.

   Contrast is pushed well past AA rather than merely to it, and type sizes start at
   17px and go up. Nothing here is dense. A person reading this is not comparing a
   week; they are answering one question.

   THE SIGNATURE: THE CALL BAND.
   Every window in this app is drawn as a band — the whole call as a faint rule,
   the person's OWN hours as a solid amber bar inside it, with mono labels at each
   end. It says the one thing §3.3 insists on and that the coordinator's grid can
   only say in words: your hours are not the call's hours. The same object, made
   draggable, IS the partial-acceptance selector (§5.4) — so "these hours inside
   those hours" looks identical whether you are reading it or choosing it. One
   device, two uses, and the second one is free because the first taught it.

   Two rules carried over from the console, deliberately, because they are right:
   every time and number is monospaced; and colour is never the only channel — each
   state carries a word as well as a hue.

   Tap targets: 48px minimum, 60px for anything that commits somebody to work.
   Primary actions sit at the bottom of the screen, in the thumb's reach, not at the
   top where a design tool puts them.
   ========================================================================== */

/* Layout tokens. Colour and type come from static/themes/. A 48px tap target
   is not a branding decision, so it does not live in a theme.

   Scoped to `html` rather than `.wrap`: the sticky commit bar and the bottom
   tab bar (`.commit`, `.tabs`) are deliberately siblings of `.wrap`, not its
   descendants — see base.html — so a custom property set on `.wrap` alone
   would not reach them and both would lose their gutter and max width. */
:where(html) {
  --tap: 48px;
  --tap-commit: 60px;
  --gutter: 1rem;
  --measure: 34rem;
}

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  /* Both bottom bars are fixed and the offer screen carries both, so the last card
     has to clear the taller case rather than the common one. Extra empty space at the
     foot of a list costs nothing; a commit button covering the last row costs the
     tap. */
  padding: 0 0 calc(9.5rem + env(safe-area-inset-bottom, 0px));
  background: var(--ground);
  color: var(--ink);
  font: 400 17px/1.5 var(--face-text);
  -webkit-font-smoothing: antialiased;
}

time,
.num,
.mono {
  font-family: var(--face-data);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* --ink, not --accent and not --own: --accent cannot be text in dark mode
   (3.40:1), but a plain link is not an ownership statement either — --own
   means "these hours are yours" (D32), and reaching for it here would just
   spread that meaning over every piece of interactive text in the app. The
   underline already marks this as a link. */
a {
  color: var(--ink);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
}

/* Amber, not the brand accent, and not by oversight: a focus ring is the one
   affordance some people navigate entirely by, and --own is 9.02:1 on the dark
   ground where --accent is 3.40:1. See D32. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--own);
  outline-offset: 2px;
  border-radius: 2px;
}

h1,
h2,
h3 {
  margin: 0;
  font-family: var(--face-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

h1 {
  font-size: 1.5rem;
}

h2 {
  font-size: 1.125rem;
}

p {
  margin: 0 0 0.75rem;
}

.skip-link {
  position: absolute;
  left: -9999px;
}

.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
  z-index: 20;
  padding: 0.5rem 0.75rem;
  background: var(--accent);
  color: var(--on-accent);
}

/* --- Shell ---------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.topline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.875rem 0 0.5rem;
  border-bottom: 1px solid var(--rule);
}

.topline__org {
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.topline__who {
  font-size: 0.8125rem;
  color: var(--ink-soft);
}

.label {
  display: block;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.muted {
  color: var(--ink-soft);
}

.faint {
  color: var(--ink-faint);
  font-size: 0.875rem;
}

.screen-head {
  padding: 1.25rem 0 0.5rem;
}

/* --- Messages ------------------------------------------------------------- */

.flash {
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.flash li {
  padding: 0.875rem 1rem;
  margin-bottom: 0.5rem;
  border-left: 4px solid var(--rule-strong);
  background: var(--ground-raised);
  font-size: 1rem;
}

.flash .success {
  border-left-color: var(--go);
  background: var(--go-wash);
}

.flash .warning {
  border-left-color: var(--hold);
  background: var(--hold-wash);
}

.flash .error {
  border-left-color: var(--stop);
  background: var(--stop-wash);
}

/* --- The call band. See the header note. --------------------------------- */

.band {
  margin: 0.75rem 0 0;
}

.band__times {
  display: flex;
  justify-content: space-between;
  font-family: var(--face-data);
  font-size: 0.8125rem;
  color: var(--ink-faint);
}

.band__track {
  position: relative;
  height: 14px;
  margin: 0.25rem 0;
  background: var(--ground-sunk);
  border: 1px solid var(--rule);
}

.band__own {
  position: absolute;
  top: -1px;
  bottom: -1px;
  /* This is the token the call band exists to consume — see _contract.css:
     "the person's own portion", not the brand. */
  background: var(--own);
}

/* A hatch as well as a hue when the person's hours are a SLICE of the call, so
   the "not the whole thing" signal survives a monochrome screenshot and a
   colour-blind reader. Not on a full booking: a hatched bar filling the whole
   track read as a warning, not as "all of it" (Andrew, 6 September 2026). */
.band--partial .band__own {
  background-image: repeating-linear-gradient(
    -45deg,
    color-mix(in srgb, var(--ground-sunk) 16%, transparent) 0 3px,
    transparent 3px 7px
  );
}

.band--cancelled .band__own {
  background: var(--rule-strong);
}

.band__caption {
  font-size: 0.8125rem;
  color: var(--ink-soft);
}

/* --- Cards ---------------------------------------------------------------- */

.card {
  display: block;
  margin: 0.75rem 0;
  padding: 1rem;
  background: var(--ground-raised);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--rule-strong);
  color: inherit;
  text-decoration: none;
}

a.card:active {
  background: var(--ground-sunk);
}

/* The next call. A flat wash and a thicker rule, not a gradient: a gradient here
   said "this is important" in the voice of every dashboard, and the size of the
   time on it already says it louder. */
.card--next {
  border-left-color: var(--own);
  border-left-width: 6px;
  background: var(--own-wash);
}

.card--offer {
  border-left-color: var(--hold);
}

.card--cancelled {
  border-left-color: var(--stop);
}

/* "Do not attend" is the single most important thing this app ever says, so the
   struck time keeps FULL ink and the strike itself carries the colour. Greying the
   digits — the obvious way to render a cancellation — made the one line that must
   survive direct sunlight the lowest-contrast text on the surface. */
.card--cancelled .card__when {
  text-decoration: line-through;
  text-decoration-thickness: 3px;
  text-decoration-color: var(--stop);
  color: var(--ink);
}

/* THE TIME IS THE HEADLINE. Crew think "6am Pioneer", not "Legally Blonde". */
.card__when {
  display: block;
  font-family: var(--face-data);
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
}

.card--next .card__when {
  font-size: 2.5rem;
}

.card__day {
  display: block;
  font-size: 0.9375rem;
  font-weight: 600;
  /* --ink-soft, not --own: a date label on every card is not an ownership
     statement, it's a supporting line — the same tier as .card__job below. */
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}

.card__where {
  display: block;
  margin-top: 0.375rem;
  font-size: 1.0625rem;
  font-weight: 600;
}

.card__job {
  display: block;
  font-size: 0.9375rem;
  color: var(--ink-soft);
}

/* The coordinator's word with the ask (D58): body ink, because unlike the job
   line it is the one thing on the card they cannot read anywhere else. */
.card__note {
  display: block;
  margin-top: 0.375rem;
  font-size: 0.9375rem;
}

.card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.625rem;
}

/* --- Tags ---------------------------------------------------------------- */

.tag {
  display: inline-block;
  padding: 0.1875rem 0.5rem;
  border: 1px solid var(--rule-strong);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.tag--go {
  border-color: var(--go);
  background: var(--go-wash);
  color: var(--go);
}

.tag--hold {
  border-color: var(--hold);
  background: var(--hold-wash);
  color: var(--hold);
}

.tag--stop {
  border-color: var(--stop);
  background: var(--stop-wash);
  color: var(--stop);
}

/* An alert raised on a person: a record beside the state tag, not the state itself. */
.tag--alert {
  border-color: var(--hold);
  background: var(--hold-wash);
  color: var(--hold);
}

.tag--lead {
  border-color: var(--own);
  color: var(--own);
}

/* --- Buttons ------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 0.625rem 1.125rem;
  border: 2px solid var(--rule-strong);
  background: transparent;
  color: var(--ink);
  font: 600 1rem/1.2 var(--face-text);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  /* Not decoration: a flex container discards the whitespace between its items, so
     `Accept <span x-text="readout">` rendered as "Accept11pm-3:45am". A gap here
     fixes it once rather than per label, and `em` keeps it proportional to the
     button's own type size. */
  gap: 0.4em;
}

.btn:active {
  background: var(--ground-sunk);
}

.btn--commit {
  width: 100%;
  min-height: var(--tap-commit);
  border-color: var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 1.1875rem;
  font-weight: 700;
}

/* --ink, not --accent and not --own, on both of these: neither has a fill
   behind its text (this one clears .btn--commit's background back to
   transparent) — accepting part of a call is an action, not an ownership
   statement, so it does not borrow --own's meaning any more than it can
   borrow --accent's dark-mode-unsafe one. */
.btn--commit-narrow {
  background: transparent;
  color: var(--ink);
}

/* --ink-soft, not --own: "quiet" is the point of this button (sign out,
   remove this) — a de-emphasised action, not a claim of ownership. */
.btn--quiet {
  min-height: var(--tap);
  border-color: transparent;
  color: var(--ink-soft);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
}

.btn--danger {
  border-color: var(--stop);
  color: var(--stop);
}

/* A control, not a label. Before this, "On site" was an outlined grey box sitting
   directly under the outlined grey "AV OP" *tag*, and on a dock a lead cannot spend
   time working out which of two identical boxes does something. Moss green is this
   palette's "committed cover", which is exactly what an arrival is. */
.btn--go {
  border-color: var(--go);
  background: var(--go-wash);
  color: var(--go);
  font-weight: 700;
}

.btn--stack {
  width: 100%;
  margin-top: 0.5rem;
}

.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.btn-row .btn {
  flex: 1 1 6rem;
}

/* --- Sticky commit bar. Thumb reach, not screen top. -------------------- */

/* Stacked ABOVE the tab bar, not on top of it. Both were fixed at `bottom: 0`,
   which on the one screen that has both — the offer response — hid the navigation
   entirely behind the accept button. */
.commit {
  position: fixed;
  inset: auto 0 calc(3.25rem + env(safe-area-inset-bottom, 0px)) 0;
  z-index: 10;
  padding: 0.75rem var(--gutter);
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--ground) 60%, transparent),
    var(--ground) 1.5rem
  );
  border-top: 1px solid var(--rule);
}

.commit__inner {
  width: 100%;
  max-width: var(--measure);
  margin: 0 auto;
}

/* --- Bottom navigation -------------------------------------------------- */

.tabs {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 9;
  display: flex;
  background: var(--ground-sunk);
  border-top: 1px solid var(--rule);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

.tabs__link {
  flex: 1;
  min-height: 3.25rem;
  display: flex;
  /* A row, not a column: stacked under its label the offer count was clipped by the
     bottom of the viewport. Beside it, "Offers 2" reads as one thing and fits. */
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  padding: 0.375rem 0.25rem;
  color: var(--ink-soft);
  font-size: 0.8125rem;
  font-weight: 600;
  text-decoration: none;
}

/* --ink throughout, not --own or --accent: "you are on this tab" is
   navigation state, not an ownership claim, and --accent can't be text here
   either way. Full --ink against the inactive tabs' --ink-soft is what
   actually marks this one as current; the indicator bar matches the text so
   the two don't mix brand colours on one control. */
.tabs__link[aria-current="page"] {
  color: var(--ink);
  box-shadow: inset 0 3px 0 var(--ink);
  background: var(--ground);
}

.tabs__count {
  display: inline-block;
  min-width: 1.375rem;
  padding: 0 0.25rem;
  background: var(--hold);
  /* --hold is a light, amber-family fill in both themes, not the brand, so its
     text pairs with --on-own rather than --on-accent (white on --accent
     wouldn't clear contrast here, since --hold is nowhere near --accent).
     This is a contrast pairing, not an ownership claim — --on-own is simply
     the only guaranteed-dark, contrast-safe text token this contract offers
     for a light fill, exactly as --on-accent would be for a crimson one. */
  color: var(--on-own);
  font-family: var(--face-data);
  font-size: 0.75rem;
  font-weight: 700;
  text-align: center;
}

/* --- Lists -------------------------------------------------------------- */

.rows {
  margin: 0.5rem 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--rule);
}

.rows > li {
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--rule);
}

.row-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}

.row-line__name {
  font-size: 1.0625rem;
  font-weight: 600;
}

.row-line__hours {
  font-family: var(--face-data);
  font-size: 0.9375rem;
  color: var(--ink-soft);
  white-space: nowrap;
}

.note {
  margin: 0.75rem 0;
  padding: 0.875rem 1rem;
  background: var(--ground-raised);
  border-left: 4px solid var(--rule-strong);
  font-size: 1rem;
}

.note--access {
  border-left-color: var(--own);
  white-space: pre-wrap;
}

.note--warn {
  border-left-color: var(--hold);
  background: var(--hold-wash);
}

.empty {
  margin: 2rem 0;
  padding: 1.5rem 1rem;
  border: 1px dashed var(--rule-strong);
  text-align: center;
  color: var(--ink-soft);
}

/* --- Forms -------------------------------------------------------------- */

.field {
  margin: 0 0 1rem;
}

.field > label {
  display: block;
  margin-bottom: 0.25rem;
  font-size: 0.9375rem;
  font-weight: 700;
}

.field input[type="text"],
.field input[type="date"],
.field input[type="time"],
.field input[type="number"],
.field select,
.field textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 0.625rem 0.75rem;
  background: var(--ground-sunk);
  border: 2px solid var(--rule-strong);
  color: var(--ink);
  font: 400 1.0625rem/1.4 var(--face-text);
}

.field input[type="date"],
.field input[type="time"],
.field input[type="number"] {
  font-family: var(--face-data);
}

.field__help {
  margin: 0.25rem 0 0;
  font-size: 0.875rem;
  color: var(--ink-faint);
}

.field__errors {
  margin: 0.375rem 0 0;
  padding: 0;
  list-style: none;
  color: var(--stop);
  font-size: 0.9375rem;
  font-weight: 600;
}

.pair {
  display: flex;
  gap: 0.75rem;
}

.pair > * {
  flex: 1;
}

/* --- The chooser: the band, made draggable (§5.4) ----------------------- */

.chooser {
  margin: 1rem 0 0;
  padding: 1rem;
  background: var(--ground-raised);
  border: 1px solid var(--rule);
}

.chooser__readout {
  display: block;
  font-family: var(--face-data);
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--own);
}

.chooser__to {
  font-family: var(--face-text);
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink-soft);
}

.chooser__track {
  position: relative;
  height: 52px;
  margin: 1.25rem 0 0.5rem;
  background: var(--ground-sunk);
  border: 1px solid var(--rule);
  touch-action: none;
}

.chooser__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  /* Translucent --own rather than the near-black wash it started as: on a dock
     in direct sun the chosen span has to be obviously the lit part of the band, and
     a dark fill between two thin rules was not legible at arm's length. */
  background: color-mix(in srgb, var(--own) 30%, transparent);
  border-left: 2px solid var(--own);
  border-right: 2px solid var(--own);
}

/* 44px of grabbable width, drawn as a 20px handle: the target is bigger than the
   mark, which is what makes it usable with a thumb in gloves. */
.chooser__handle {
  position: absolute;
  top: -8px;
  bottom: -8px;
  width: 44px;
  margin-left: -22px;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: ew-resize;
  touch-action: none;
}

.chooser__handle::after {
  content: "";
  position: absolute;
  inset: 0 12px;
  background: var(--own);
  border: 2px solid var(--on-own);
}

.chooser__handle:focus-visible::after {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}

.chooser__ends {
  display: flex;
  justify-content: space-between;
  font-family: var(--face-data);
  font-size: 0.8125rem;
  color: var(--ink-faint);
}

.chooser__nudge {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.chooser__nudge .btn {
  min-width: var(--tap);
  padding: 0.5rem;
  font-family: var(--face-data);
  font-size: 1.25rem;
}

/* The label takes the slack so the two buttons stay TOGETHER on the right, within
   one thumb's arc. Split to opposite edges — which is what `flex: 1` on the label
   between them gives you — they are a two-handed control on a one-handed surface. */
.chooser__nudge span:first-child {
  flex: 1;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink-soft);
}

/* --- Countdown ---------------------------------------------------------- */

.countdown {
  font-family: var(--face-data);
  font-weight: 700;
  color: var(--hold);
}

.countdown--urgent {
  color: var(--stop);
}

/* --- Details/summary disclosure ---------------------------------------- */

details.disclose {
  margin-top: 0.75rem;
  border-top: 1px solid var(--rule);
}

/* Inside a crew row the two disclosures ("Running late", "Not here at all") are
   already separated by the button row's own gap, so their margin was doubling it —
   three crew members filled a screen and a half. Tap targets stay 48px; only the
   dead space between them goes. A lead with eight crew has to be able to see the
   list. */
.card .btn-row > details.disclose {
  margin-top: 0;
}

.card .btn-row > details.disclose > summary {
  min-height: var(--tap);
  padding: 0.5rem 0;
}

details.disclose > summary {
  min-height: var(--tap);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 0;
  /* --ink, not --own: "I can only do part of it", "Running late" and the
     rest are things a person does, not a claim on hours that are theirs.
     Full --ink, not --ink-soft, keeps the toggle discoverable on a dock in
     direct sun; the 600 weight already carries most of the emphasis. */
  color: var(--ink);
  font-weight: 600;
  cursor: pointer;
}

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

details.disclose > summary::before {
  content: "+";
  font-family: var(--face-data);
  font-size: 1.25rem;
}

details.disclose[open] > summary::before {
  content: "−";
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

/* A wider screen is a coordinator glancing at it or a tablet on a bench. It gets
   the same single column, centred — not a second layout to maintain for a surface
   whose entire premise is one hand and one question. */
@media (min-width: 44rem) {
  body {
    font-size: 17.5px;
  }
}

/* --- Offer cards with an action row ---------------------------------------- */
/* The card used to be one link. It is now a link (the detail screen) plus an
   action row, so a yes to the whole call is one tap from the list. */
.card--offer .card__link {
  display: block;
  color: inherit;
  text-decoration: none;
}

.card__actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
}

.btn--wide {
  width: 100%;
}

.accept-all {
  margin: 0.75rem 0;
}

.accept-all .faint {
  margin: 0.4rem 0 0;
  font-size: 0.8125rem;
}

/* A compact button for inline rows — the tick on a task, a per-row action. Still
   a real target (2.5rem), just not the full-width commit size. */
.btn--small {
  min-height: 2.5rem;
  padding: 0.35rem 0.75rem;
  font-size: 0.9rem;
}
