/* Tokens. Nothing in this application writes a hex value outside this block.
   Taken from infinityrp.com -- see docs/superpowers/specs/2026-09-30-new-store-schedule-design.md

   The three derived values at the bottom are mixed from the tokens above them
   rather than written as new hex, so the rule above stays literally true and
   each one's provenance is visible. */
:root {
  --ink:   #010101;  /* wordmark, body text */
  --teal:  #1D505B;  /* brand. 9:1 on white, so it may carry text */
  --camel: #b48c67;  /* accent. 3:1 -- large text and decoration ONLY */
  --mist:  #a6aab1;  /* borders, chevron, decorative rules. 2.33:1 on paper -- NEVER text */
  --slate: #6a6d71;  /* secondary text: labels, help, counts, dates. 5.2:1 on paper,
                         4.89:1 on panel, 4.57:1 on teal-wash -- the readable sibling of mist */
  --paper: #ffffff;
  --panel: #f8f8f7;
  --hairline: #ececea;  /* borders between rows and tiles */

  /* Status is its own system and never borrows the brand. On this product
     red/amber/green is the feature, not decoration. */
  --on-time: #1b7f4b;
  --at-risk: #a8690b;
  --late:    #b3261e;

  /* The quiet ground under a chart track, a table head and a resting tile.
     Teal at 8% rather than a grey, so even the quiet surfaces are the brand's. */
  --teal-wash: color-mix(in srgb, var(--teal) 8%, var(--paper));
  /* Secondary text reversed out of a solid teal field. 6.2:1 on --teal. */
  --teal-ink:  color-mix(in srgb, var(--paper) 78%, var(--teal));
  /* Amber, but only ever as a solid field carrying reversed text. Raw
     --at-risk under white measures 4.48:1, a hair under AA for the tile's
     label; darkened toward the ink it measures 5.3:1. It is never used for
     text, so the status trio above is unchanged. */
  --at-risk-field: color-mix(in srgb, var(--at-risk) 90%, var(--ink));

  /* Infinity's display face is HK Grotesk Light -- the same family as their
     body text, set large and light. Hanken Grotesk is its open release, so
     one family covers both roles. The serif is the wordmark lockup and
     nothing else: it is the only serif on infinityrp.com too. */
  --sans:  "Hanken Grotesk", system-ui, sans-serif;
  --serif: "Fraunces", Georgia, serif;          /* the INFINITY/GLOBAL lockup ONLY */
  --mono:  ui-monospace, SFMono-Regular, Menlo, monospace;

  --control: 2.1rem;  /* one height for every input, select and button */
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  margin: 0;
}

/* Their mosaic is hard-edged squares. Nothing in this application is rounded:
   one radius on everything is the thing that makes an interface look
   generated, and no radius at all is the brand. */
*, *::before, *::after { border-radius: 0; }

/* Display type. Light and large, left-aligned, slightly tightened -- the same
   setting as the headline on their homepage, in the same family as the body
   text. The old rule put a serif here; see the design spec, section 9. */
h1 {
  font-weight: 200;
  font-size: clamp(1.9rem, 1.4rem + 1.2vw, 2.5rem);
  letter-spacing: -0.02em;
  line-height: 1.08;
  margin: 0 0 0.75rem;
}
h2 { font-weight: 300; font-size: 1.15rem; letter-spacing: -0.01em; margin: 0 0 0.5rem; }
h3 { font-weight: 500; font-size: 1rem; margin: 0 0 0.4rem; }

/* Every figure in the application lines up in a column: counts, due dates,
   table values. */
.figure, .count, .listing td, .schedule td { font-variant-numeric: tabular-nums; }

/* Ship IDs, IF numbers, sales order numbers. Positional identifiers --
   structural rather than decorative. */
.identifier { font-family: var(--mono); font-size: 0.85em; letter-spacing: -0.01em; }

/* ------------------------------------------------------------------ *
 * Controls. One height, one border, one focus ring -- the browser's own
 * chrome for a <select> and a date field was most of why the store page
 * looked unfinished.
 * ------------------------------------------------------------------ */
input[type=email], input[type=password], input[type=text], input[type=date],
input[type=time], input[type=number], select, textarea {
  font: inherit; font-size: 0.9rem; color: var(--ink); background: var(--paper);
  border: 1px solid var(--mist); padding: 0 0.55rem; height: var(--control);
  box-sizing: border-box;
}
textarea { padding: 0.45rem 0.55rem; height: auto; line-height: 1.5; }

/* The chevron is drawn from the mist token with two gradients rather than
   fetched as an image, so it stays inside the no-loose-colours rule and
   there is no asset to go missing. */
select {
  appearance: none; padding-right: 2rem;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--mist) 50%),
    linear-gradient(135deg, var(--mist) 50%, transparent 50%);
  background-size: 6px 6px, 6px 6px;
  background-position: right 1.1rem center, right 0.73rem center;
  background-repeat: no-repeat;
}
select:hover { border-color: var(--teal); }
input[type=date], input[type=time] { padding-right: 0.3rem; }
::-webkit-calendar-picker-indicator { opacity: 0.4; cursor: pointer; }
::-webkit-calendar-picker-indicator:hover { opacity: 1; }
input[type=checkbox] { accent-color: var(--teal); width: 0.95rem; height: 0.95rem;
                       vertical-align: -0.1em; margin: 0 0.35rem 0 0; }

/* Keyboard focus has to stay visible: the grid's cell editing is
   keyboard-operable by design and there are browser examples asserting it. */
input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--teal); outline-offset: 1px;
}

input[type=submit], button[type=submit] {
  font: inherit; font-size: 0.9rem; font-weight: 500; cursor: pointer; height: var(--control);
  background: var(--teal); color: var(--paper);
  border: 1px solid var(--teal); padding: 0 1.1rem;
}
input[type=submit]:hover, button[type=submit]:hover {
  background: var(--ink); border-color: var(--ink);
}

/* ------------------------------------------------------------------ *
 * The wordmark lockup
 *
 * Infinity's own logotype, as it is set on their site and nowhere else in
 * this application: INFINITY in letterspaced serif caps, a rule under it,
 * GLOBAL small beneath. Caps come from text-transform rather than from the
 * markup so the accessible name stays "Infinity Global".
 * ------------------------------------------------------------------ */
.wordmark { display: inline-grid; justify-items: center; line-height: 1; text-decoration: none; }
.wordmark-name {
  font-family: var(--serif); font-weight: 400; font-size: 1rem;
  text-transform: uppercase; letter-spacing: 0.22em; text-indent: 0.22em;
}
.wordmark-sub {
  font-family: var(--serif); font-weight: 400; font-size: 0.48rem;
  text-transform: uppercase; letter-spacing: 0.3em; text-indent: 0.3em;
  border-top: 1px solid currentColor; margin-top: 0.22rem; padding-top: 0.2rem;
  width: 100%; text-align: center;
}

/* ------------------------------------------------------------------ *
 * The nav: present on every page, including signed out, so the wordmark
 * gives the sign-in page its branding. The needs-you count and sign-out
 * button are the only pieces that require a signed-in Current.user.
 * ------------------------------------------------------------------ */
.chrome {
  display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap;
  padding: 0.6rem 1.5rem; border-bottom: 1px solid var(--hairline);
}
.chrome a { color: var(--ink); text-decoration: none; font-size: 0.9rem; }
.chrome .nav-links { display: flex; gap: 1.25rem; flex-wrap: wrap; align-items: center;
                     padding-left: 1.25rem; border-left: 1px solid var(--hairline); }
.chrome .nav-links a { padding: 0.2rem 0; border-bottom: 2px solid transparent; }
.chrome .nav-links a:hover { color: var(--teal); border-bottom-color: var(--teal); }
.chrome .wordmark { color: var(--ink); }

/* The most-glanced element in the application, so it is a signal rather than
   a grey pill: a solid field of the status red while anything is behind, and
   a quiet outline the moment nothing is. Same size either way, so the nav
   does not reflow when the number changes. */
.chrome .needs-you {
  margin-left: auto; padding: 0.3rem 0.65rem; font-size: 0.85rem; font-weight: 500;
}
.needs-you[data-behind="true"]  { background: var(--late); color: var(--paper); }
.needs-you[data-behind="false"] { color: var(--slate); box-shadow: inset 0 0 0 1px var(--hairline); }
.chrome .linklike { background: none; border: none; font: inherit; font-size: 0.9rem;
                    color: var(--slate); cursor: pointer; padding: 0; }
.chrome .linklike:hover { color: var(--teal); }

/* ------------------------------------------------------------------ *
 * Viewing as somebody else. A solid field of --ink across the top of every
 * page, which is the one surface in the application set that way.
 *
 * Not red and not teal, and both omissions are the design. Red, amber and
 * green are this product's feature -- they mean a store is behind -- so a red
 * bar on every page would be the loudest status on screen saying nothing
 * about any store. Teal is the brand doing structural work. This is neither a
 * status nor chrome: it is the application saying it is not behaving
 * normally, and black is the only thing in the token set that says that.
 *
 * Sticky, where .chrome below it is not. The banner is the one statement that
 * stays true however far anybody has scrolled, and the way out of somebody
 * else's account must never be a scroll away.
 * ------------------------------------------------------------------ */
.viewing-as {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap;
  background: var(--ink); color: var(--paper);
  padding: 0.55rem 1.5rem;
}
.viewing-as p { margin: 0; font-size: 0.85rem; font-weight: 300; max-width: 80ch; }
.viewing-as strong { font-weight: 600; }
/* Reversed out of the field rather than filled: a solid button inside a solid
   bar is two fields fighting, and the sentence beside it is what carries the
   message. Outlined so it still reads as pressable. */
.viewing-as form { margin: 0; margin-left: auto; }
/* Two class selectors, because the global `input[type=submit]` rule is an
   element plus an attribute and outweighs a single class -- which is how this
   first rendered as a solid teal button inside the black bar, the one colour
   the banner is deliberately not using. */
.viewing-as .viewing-as-exit {
  background: none; color: var(--paper); border: 1px solid var(--paper);
  font: inherit; font-size: 0.85rem; font-weight: 500; white-space: nowrap;
  padding: 0.3rem 0.7rem; height: auto; cursor: pointer;
}
.viewing-as .viewing-as-exit:hover { background: var(--paper); color: var(--ink); }

/* Sticky reserves the banner's own height in the flow exactly once, at its
   natural position -- it does not reserve that height again every time the
   page scrolls past it, so without help, scrolling slides whatever is now at
   the top of the document under the pinned banner. On a long store page that
   is a row of the change history, permanently, once the page is scrolled as
   far as it goes: there is no further scrolling left to bring it clear.
   --banner-height is measured off the banner itself (see the Stimulus
   controller), so this follows it wrapping to more lines at a narrow width
   rather than a number that would be wrong the moment it did.

   scroll-padding-top covers the other half: any native scroll-into-view --
   following an anchor, Tab landing on a focusable element, Capybara driving a
   click -- that would otherwise land its target under the banner the same
   way.

   :has() rather than a body class: Current.impersonating? already decides
   whether the banner renders, and this reads that decision off the page
   instead of a second place having to agree with it. Harmless when nobody is
   being viewed as -- the fallback is 0px and the padding this adds is zero. */
html { scroll-padding-top: var(--banner-height, 0px); }
body:has([data-impersonation-banner]) .page {
  padding-bottom: calc(100vh - var(--banner-height, 0px) + 6rem);
}

/* The page. The layout was a bare yield against `body { margin: 0 }`, so
   every heading, table and form started hard against the viewport edge while
   the nav was inset. The schedule is wide, hence the generous ceiling. */
.page { padding: 1.5rem; max-width: 100rem; margin: 0 auto; }

/* ------------------------------------------------------------------ *
 * The dashboard board.
 *
 * The one screen with room, so it carries the brand: a mosaic with a solid
 * teal field at its head and the three tiles stepping down the right as the
 * alternating squares. Everything else in the application stays quiet.
 *
 * Saturation follows need. A tile is a solid field while its count is above
 * zero and a quiet outline when it is not, so the page looks different on a
 * bad morning to a good one, which identical cards never would. Red and amber
 * are the status system. The third tile is the brand's own teal, because this
 * week's load is work in front of somebody rather than a warning -- it used to
 * be "9 on time", which is the absence of news and never went solid at all.
 * ------------------------------------------------------------------ */
.board {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(11rem, 15rem);
  gap: 1px;
  background: var(--hairline);   /* the 1px gap, drawn */
  border: 1px solid var(--hairline);
  margin-bottom: 2rem;
}
.board > * { background: var(--paper); }
/* Placed rather than flowed, so the source order can be the reading order --
   the situation, then the counts, then the detail -- while the picture stays a
   checkerboard. Churn is the full width of the bottom row: it is the panel
   nothing else in the application says, and width is how a page says so
   without a colour or a badge. */
.board-lede      { grid-column: 1 / 3; grid-row: 1; }
.tile-late       { grid-column: 3;     grid-row: 1; }
.board-queue     { grid-column: 1;     grid-row: 2; }
.board-behind    { grid-column: 2;     grid-row: 2; }
.tile-at_risk    { grid-column: 3;     grid-row: 2; }
.board-ahead     { grid-column: 1;     grid-row: 3; }
.board-churn     { grid-column: 2;     grid-row: 3; }
.tile-this_week  { grid-column: 3;     grid-row: 3; }
/* FieldPolicy does not show next_step to the distribution centre or the
   viewer, so those two roles have no work queue. The late listing takes the
   width it would have had rather than leaving a hole in the mosaic. */
.board-without-queue .board-behind { grid-column: 1 / 3; }

.board-lede { background: var(--teal); color: var(--paper); padding: 1.75rem; display: flex;
              flex-direction: column; gap: 0.5rem; min-height: 11rem; }
.board-lede h1 { color: var(--teal-ink); font-size: 0.95rem; font-weight: 500;
                 letter-spacing: 0; margin: 0; }
.board-lede .lede-line { font-size: clamp(1.5rem, 1rem + 1.4vw, 2.1rem); font-weight: 200;
                         letter-spacing: -0.02em; line-height: 1.15; margin: 0; max-width: 30ch; }
.board-lede .lede-note { color: var(--teal-ink); font-size: 0.9rem; margin: 0; max-width: 60ch;
                         line-height: 1.55; }
/* There was a "Saturday 3 October 2026" under this. It was the one element on
   the screen that carried no state and changed nothing -- it was there to make
   the teal field look composed. The schedule already groups by Today and
   Tomorrow and every date on the page is written out in full, so it was
   decoration. Cut. */

/* The tiles themselves. .tile/.tile-<name>/.figure/.label are the names the
   dashboard request example reads, so they stay. */
/* Top-aligned, not centred: the figure then sits on the same line as the
   heading of the panel beside it, so the mosaic reads as composed rather than
   as three numbers floating in whatever height the row happened to be. */
.tile { padding: 1.25rem; display: flex; flex-direction: column; justify-content: flex-start;
        min-height: 9rem; }
.tile .figure { display: block; font-size: 3.4rem; font-weight: 200; line-height: 1;
                letter-spacing: -0.03em; }
.tile .label { font-size: 0.9rem; font-weight: 500; margin-top: 0.3rem; }
.tile .gloss { font-size: 0.8rem; margin-top: 0.15rem; line-height: 1.35; }

/* Resting: nothing of this kind needs anybody. */
.tile { color: var(--slate); }
.tile .label { color: var(--ink); }
.tile .gloss { color: var(--slate); }
/* Live: a solid field. */
.tile-at_risk.is-live    { background: var(--at-risk-field); }
.tile-late.is-live       { background: var(--late); }
.tile-this_week.is-live  { background: var(--teal); }
.tile.is-live, .tile.is-live .label { color: var(--paper); }
.tile.is-live .gloss { color: var(--paper); opacity: 0.8; }
/* Resting, the week tile keeps the teal wash the quiet surfaces use, so an
   empty week still reads as part of the mosaic rather than as a hole in it. */
.tile-this_week { background: var(--teal-wash); }

.board-panel { padding: 1.25rem 1.5rem; }
.board-panel h2 { margin-bottom: 0.4rem; }
/* What the panel's numbers are of, said once, under the heading. Same measure
   and colour as an empty state, because it does the same job: it is the
   sentence that makes the figures beneath it mean something. */
.panel-note { color: var(--slate); font-size: 0.85rem; line-height: 1.5;
              margin: 0 0 0.75rem; max-width: 70ch; }

/* The work queue. A lined listing with the count in its own column so the
   piles can be compared by reading straight down, and the whole line is the
   link, because the count and the job are one thing to click. */
.queue { list-style: none; margin: 0; padding: 0; }
.queue li { border-top: 1px solid var(--hairline); }
.queue li:first-child { border-top: none; }
.queue a { display: grid; grid-template-columns: 2.25rem minmax(0, 1fr) auto; gap: 0.75rem;
           align-items: baseline; padding: 0.5rem 0; color: var(--ink); text-decoration: none; }
.queue a:hover .queue-task { color: var(--teal); }
.queue .queue-count { font-size: 1.5rem; font-weight: 200; line-height: 1;
                      font-variant-numeric: tabular-nums; text-align: right; color: var(--teal); }
.queue .queue-task { font-size: 0.95rem; font-weight: 500; }
/* Which pile to start with. The status red, because this is the same judgement
   the tiles and the schedule make, and it is the only coloured thing in the
   panel so it cannot be missed. */
.queue .queue-behind { color: var(--late); font-size: 0.8rem; font-weight: 500; }

/* Behind schedule: a lined listing, not a bulleted list with underlined
   links. Each row carries the red spine the schedule uses for the same
   judgement, so the two screens agree about what "behind" looks like. */
.behind { list-style: none; margin: 0; padding: 0; }
.behind li { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 1rem;
             align-items: baseline; padding: 0.45rem 0 0.45rem 0.75rem;
             border-top: 1px solid var(--hairline); box-shadow: inset 3px 0 var(--late); }
.behind li:first-child { border-top: none; }
.behind a { color: var(--ink); text-decoration: none; font-weight: 500; }
.behind a:hover { color: var(--teal); }
.behind .behind-status { color: var(--slate); font-size: 0.85rem; }
.behind .behind-due { color: var(--late); font-size: 0.85rem; font-weight: 500; }

/* The four weeks ahead. A ruled table: with four weeks and three kinds of work
   the number is the fact, and a bar beside each one was a second drawing of
   the same quantity. The week to worry about is named in the sentence above
   and marked here with a teal spine -- which is what the bars were for and
   never said out loud. */
.ahead { border-collapse: collapse; width: 100%; }
.ahead th, .ahead td { padding: 0.4rem 0.75rem; border-top: 1px solid var(--hairline);
                       font-size: 0.9rem; }
.ahead thead th { border-top: none; border-bottom: 1px solid var(--mist);
                  color: var(--slate); font-size: 0.8rem; font-weight: 500; }
.ahead tbody th { text-align: left; font-weight: 500; }
.ahead .numeric { text-align: right; font-variant-numeric: tabular-nums; }
.ahead td { font-weight: 400; }
/* A week with nothing in it is a fact worth keeping and not worth shouting. */
.ahead td.is-none { color: var(--mist); }
/* Marked only when one week is heavier than the other three. A spine down
   three of four rows says "all of these" where it is meant to say "this one",
   so a tie is said in the sentence above and drawn nowhere. */
.ahead tbody tr.is-heaviest th { box-shadow: inset 3px 0 var(--teal); padding-left: 0.5rem;
                                 color: var(--teal); font-weight: 600; }
.ahead tbody tr.is-heaviest th, .ahead tbody tr.is-heaviest td { background: var(--teal-wash); }

/* Date churn. A lined entry per store, two lines each: which store and how
   often on the first, which date it was and when on the second. Three columns
   on one line needed a measure this panel does not have, and a store name
   wrapping under its own count is worse than a second line meant on purpose. */
.churn { list-style: none; margin: 0; padding: 0; }
.churn li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.1rem 1rem;
            align-items: baseline; padding: 0.55rem 0;
            border-top: 1px solid var(--hairline); }
.churn li:first-child { border-top: none; }
.churn a { color: var(--ink); text-decoration: none; font-weight: 500; }
.churn a:hover { color: var(--teal); }
.churn .churn-count { font-variant-numeric: tabular-nums; font-weight: 600; font-size: 0.9rem;
                      white-space: nowrap; }
.churn .churn-latest { grid-column: 1 / -1; color: var(--slate); font-size: 0.85rem; }
.churn-more { color: var(--slate); font-size: 0.85rem; margin: 0.75rem 0 0; }

/* ------------------------------------------------------------------ *
 * The schedule.
 * ------------------------------------------------------------------ */
/* Heading and filters on one line while there is room. */
.screen-head { display: flex; align-items: flex-end; justify-content: space-between;
               gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.screen-head h1 { margin: 0; }
.filters { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }

/* Each bucket is a band of the page, marked by one solid teal square: the
   checkerboard at the scale a dense table can carry it. */
.bucket { border-top: 1px solid var(--hairline); padding-top: 0.9rem; margin-top: 1.25rem; }
.bucket:first-of-type { border-top: none; padding-top: 0; margin-top: 0; }
.bucket h2 { font-size: 1rem; font-weight: 500; display: flex; gap: 0.5rem;
             align-items: center; margin-bottom: 0.6rem; }
.bucket h2::before { content: ""; width: 0.7rem; height: 0.7rem; background: var(--teal); flex: none; }
.bucket .count { font-weight: 400; color: var(--slate); }
/* An empty bucket is one line, not four. Density is the point of this screen. */
.bucket.is-empty { display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; }
.bucket.is-empty h2 { margin-bottom: 0; }
.bucket.is-empty .empty { margin: 0; font-size: 0.9rem; }

/* The schedule is twelve columns wide and is not going to become a stack of
   cards: the job is comparing dates down a column. At narrow widths it scrolls
   sideways with the store column pinned, so the row a reader is reading never
   leaves the screen. Fixed layout so all six bucket tables share one column
   grid and read as a single ruled page rather than six tables that each sized
   themselves. */
.schedule-scroll { overflow-x: auto; }
.schedule { width: 100%; border-collapse: collapse; font-size: 0.875rem;
            table-layout: fixed; min-width: 62rem; }
/* A rule under the head rather than a filled band: there are six of these
   tables down the page and six grey bands read as stripes rather than as
   structure. */
.schedule th { text-align: left; font-weight: 500; color: var(--slate); padding: 0.3rem 0.6rem 0.4rem;
               font-size: 0.8rem; line-height: 1.2; vertical-align: bottom;
               background: var(--paper); border-bottom: 1px solid var(--ink); }
.schedule td { padding: 0.4rem 0.6rem; border-top: 1px solid var(--hairline); }
.schedule tbody tr:hover td { background: var(--panel); }
.schedule col.col-store { width: 15rem; }
.schedule col.col-shipment_type { width: 6.5rem; }
.schedule col.col-status { width: 6.5rem; }
.schedule col.col-next_step { width: 8rem; }
.schedule col.col-ship_id { width: 7.5rem; }

/* The pinned store column, and the health spine on it. Health reads as a left
   border on the row, not as coloured text: status is its own system and
   red/amber/green has to be findable without turning the grid into a wall of
   colour. The second shadow is the pinned column's own right edge, which only
   shows once the table is scrolled. */
.schedule th:first-child, .schedule td:first-child { position: sticky; left: 0; z-index: 1; }
.schedule td:first-child { background: var(--paper); box-shadow: 1px 0 0 var(--hairline); }
.schedule tbody tr:hover td:first-child { background: var(--panel); }
.schedule tr.health-at_risk td:first-child { box-shadow: inset 3px 0 var(--at-risk), 1px 0 0 var(--hairline); }
.schedule tr.health-late    td:first-child { box-shadow: inset 3px 0 var(--late),    1px 0 0 var(--hairline); }
.schedule td:first-child a { color: var(--ink); text-decoration: none; font-weight: 500; }
.schedule td:first-child a:hover { color: var(--teal); text-decoration: underline; }
.schedule .store-name { display: block; color: var(--slate); font-size: 0.8rem; }
.blank { color: var(--slate); }

/* Status and next-step pills. Every one of these rendered identical grey,
   because only `.pill` was ever defined.

   Status is a lifecycle, not a judgement, so it reads as a four-step
   progression in ink, slate and camel. Green, amber and red stay reserved for
   health -- the thing a reader is scanning the grid for -- and the brand teal
   stays out of both. The camel appears only as a fill and a border, never
   carrying the text: it measures 3:1.

   The four next steps are not a progression and have no ranking between them,
   so colouring them differently would invent one. They share a camel outline
   that marks the class of thing: something is waiting for a person. */
.pill { display: inline-block; padding: 0.1rem 0.45rem;
        background: var(--panel); border: 1px solid transparent; font-size: 0.8em;
        white-space: nowrap; line-height: 1.5; }

.pill-pending    { color: var(--slate); }
.pill-released   { color: var(--ink); background: color-mix(in srgb, var(--camel) 14%, var(--paper));
                   border-color: color-mix(in srgb, var(--camel) 45%, var(--paper)); }
.pill-in_transit { color: var(--ink); background: color-mix(in srgb, var(--camel) 30%, var(--paper));
                   border-color: var(--camel); }
.pill-delivered  { color: var(--paper); background: var(--ink); border-color: var(--ink); }

.pill-bill_freight, .pill-final_packing_list, .pill-manager_box, .pill-non_fills {
  background: var(--paper); border-color: var(--camel); color: var(--ink);
}

/* Shipment type is context, not state: it says what kind of job this is and
   should not compete with either of the two signals beside it. */
.pill-new_store, .pill-expansion, .pill-remodel, .pill-relocation {
  background: var(--panel); color: var(--slate);
}

/* Inline cell editing: a display span until clicked, then a form. */
/* !important deliberately, and the only one in this file: .hidden is the
   switch that closes every cell on the schedule, and any later rule giving
   one of those elements a display value wins on source order and opens all
   of them at once. A utility class whose whole job is to hide something has
   to actually hide it. */
.hidden { display: none !important; }
.cell-form { display: flex; gap: 0.25rem; align-items: center; }
.cell-form input, .cell-form select {
  font: inherit; font-size: 0.85rem; height: 1.7rem; min-width: 0; width: 100%;
  border: 1px solid var(--teal); padding: 0.1rem 0.3rem;
  background: var(--paper); color: var(--ink);
}
/* Save and Cancel sit inside the open cell. They are the visible commit --
   a select has no implicit submission, so Enter is not a commit path for
   every kind of cell and cannot be the only one. */
/* The chevron the base rule draws sits 1rem in from the right, which inside a
   grid cell this narrow lands on top of the value. */
.cell-form select { padding-right: 1.4rem;
                    background-position: right 0.75rem center, right 0.4rem center; }
.cell-save, .cell-cancel { font: inherit; font-size: 0.75rem; padding: 0.15rem 0.35rem;
                           cursor: pointer; white-space: nowrap; }
.cell-save { background: var(--teal); color: var(--paper); border: 1px solid var(--teal); }
.cell-cancel { background: var(--paper); color: var(--slate); border: 1px solid var(--hairline); }
.cell-cancel:hover { color: var(--ink); }
.cell [data-cell-target="display"] { display: block; }
.cell [data-cell-target="display"]:hover {
  box-shadow: inset 0 -1px 0 var(--teal); cursor: text;
}
.cell [data-cell-target="display"]:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }

/* ------------------------------------------------------------------ *
 * The store page: every field the signed-in role may see, in the groups the
 * field inventory uses, with the derived dates carrying their formulas and
 * the change history underneath.
 * ------------------------------------------------------------------ */
/* The masthead. The customer gets a small solid teal mark rather than being
   joined to the store with a middle dot: whose board this is, said the way
   the brand says things. */
.store-head { margin-bottom: 1.5rem; }
.store-head .customer-mark {
  display: inline-block; background: var(--teal); color: var(--paper);
  font-size: 0.75rem; font-weight: 500; padding: 0.22rem 0.55rem; margin: 0 0 0.6rem;
}
.store-head h1 { margin-bottom: 0.2rem; }
.store-head .store-name { color: var(--slate); font-size: 1rem; margin: 0; }

.standing { margin: 1rem 0 1.5rem; }
.standing p { margin: 0.25rem 0; }
.standing .meta { color: var(--slate); font-size: 0.9rem; max-width: 60ch; }
/* One treatment for the two chips: same shape, same metrics, same baseline.
   The fill is the only thing that differs, and it carries the same meaning it
   carries on the dashboard -- a solid field is something that needs you.
   Health is the signal, so it comes first and it is the one that can be
   solid; status is a lifecycle marker and stays outlined, keeping the
   four-step colouring the grid uses for it. */
.standing-chips { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.standing-chips .pill, .standing-chips .signal {
  font-size: 0.85rem; line-height: 1.5; padding: 0.25rem 0.6rem; border: 1px solid transparent;
}
.signal { font-weight: 500; }
.signal-on_time { color: var(--on-time); border-color: var(--on-time); background: var(--paper); }
.signal-at_risk { color: var(--paper); background: var(--at-risk-field); border-color: var(--at-risk-field); }
.signal-late    { color: var(--paper); background: var(--late);          border-color: var(--late); }

/* A stated fact, not a warning: the shipment may be perfectly healthy. So it
   gets the panel and the accent rule rather than any of the status colours,
   which on this product mean "this needs you". */
.standing .slip {
  background: var(--panel);
  border-left: 3px solid var(--camel);
  padding: 0.6rem 0.9rem;
  font-size: 0.9rem;
  max-width: 48rem;
}

.detail-form { max-width: 60rem; }
.field-group { margin-bottom: 2rem; }
.field-group h2 { font-size: 1rem; font-weight: 600; padding-bottom: 0.4rem;
                  border-bottom: 1px solid var(--ink); margin-bottom: 0; }
.field-group dl { margin: 0; }
.field { display: grid; grid-template-columns: 14rem minmax(0, 1fr); gap: 0 1.5rem;
         padding: 0.7rem 0; border-top: 1px solid var(--hairline); align-items: start; }
.field-group dl > .field:first-child { border-top: none; }
.field dt { color: var(--slate); font-weight: 500; font-size: 0.9rem; padding-top: 0.35rem; }
.field dt label { color: inherit; font-weight: inherit; }
.field dd { margin: 0; max-width: 34rem; }
.field dd .help { margin-bottom: 0; }
.field dd > input[type=text], .field dd > input[type=email], .field dd > input[type=number] { width: 18rem; max-width: 100%; }
.field dd > select { width: 18rem; max-width: 100%; }
.field textarea { width: 100%; box-sizing: border-box; }
.formula { display: block; color: var(--slate); font-size: 0.85em; }
/* The override is the deliberate exception to the formula above it --
   teal carries the weight so it reads at a glance, not camel, which is
   too low-contrast to trust at this size. */
.override { display: block; color: var(--teal); font-weight: 600; font-size: 0.85em; }
.override-toggle { display: block; margin-top: 0.4rem; font-size: 0.85em; }
.override-toggle label { color: var(--slate); }
.detail-form input[type=submit] { margin-top: 0.5rem; }

.history { margin-top: 2.5rem; max-width: 60rem; }
.history .empty { color: var(--slate); }
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { padding: 0.55rem 0 0.55rem 0.9rem; border-top: 1px solid var(--hairline);
               box-shadow: inset 2px 0 var(--hairline); }
.timeline li:first-child { border-top: none; }
.timeline .meta { display: block; color: var(--slate); font-size: 0.85em; margin-top: 0.1rem; }
/* Time and author, separated by a rule rather than by a middle dot. */
.timeline .meta .author { border-left: 1px solid var(--hairline); margin-left: 0.5rem; padding-left: 0.5rem; }

/* ------------------------------------------------------------------ *
 * The calendar: ship and delivery dates on a month grid.
 * ------------------------------------------------------------------ */
.month-nav { display: flex; gap: 1rem; margin-bottom: 1rem; font-size: 0.9rem; }
.calendar { width: 100%; border-collapse: collapse; table-layout: fixed; }
.calendar th { text-align: left; font-weight: 500; font-size: 0.8rem; color: var(--slate);
               padding: 0.3rem 0.3rem 0.4rem; border-bottom: 1px solid var(--ink); }
.calendar td { border: 1px solid var(--hairline); vertical-align: top; height: 5rem; padding: 0.3rem; }
.calendar td.outside { background: var(--panel); }
/* Today, as a solid teal square on the day number rather than a tinted cell:
   a whole tinted day would compete with the events inside it. */
.calendar td.is-today .daynum { background: var(--teal); color: var(--paper); padding: 0.1rem 0.3rem; }
.calendar .daynum { color: var(--slate); font-size: 0.75rem; }
.event { display: block; font-size: 0.75rem; text-decoration: none; color: var(--teal);
         margin-top: 0.15rem; }
.event:hover { text-decoration: underline; }
.event-delivers { border-left: 3px solid var(--camel); padding-left: 0.25rem; }
/* The day it actually arrived, which only appears when it is not the day it
   was promised -- dotted and italic so the two are not read as one event
   duplicated. */
.event-arrived {
  border-left: 3px dotted var(--camel);
  padding-left: 0.25rem;
  font-style: italic;
}

/* ------------------------------------------------------------------ *
 * The timeline: each shipment's span from order release to delivery, as a
 * bar against a dated axis. Named .shipment-timeline, not .timeline -- that
 * class already belongs to the change-history list on the store page, and the
 * two layouts would collide under one name.
 *
 * A table, not a list of bars. The bar is a picture of two dates, and those
 * two dates have to be readable as text as well -- a span positioned with CSS
 * percentages is invisible to a screen reader and unreadable to anyone asking
 * "when", which is the question a timeline is opened for. So the dates are
 * columns and the chart is the last one.
 *
 * The axis head and every track cell carry no horizontal padding, which is
 * what makes `left: n%` mean the same place in both: a tick and the bar under
 * it are measured against the same box.
 * ------------------------------------------------------------------ */
.timeline-window { color: var(--slate); font-size: 0.9rem; max-width: 70ch; margin: 0 0 1rem; }
.timeline-scroll { overflow-x: auto; }
.shipment-timeline { width: 100%; border-collapse: collapse; font-size: 0.875rem;
                     table-layout: fixed; min-width: 56rem; }
.shipment-timeline th { text-align: left; font-weight: 500; color: var(--slate);
                        font-size: 0.8rem; line-height: 1.2; padding: 0.3rem 0.6rem 0.4rem;
                        vertical-align: bottom; background: var(--paper);
                        border-bottom: 1px solid var(--ink); }
.shipment-timeline td { padding: 0.45rem 0.6rem; border-top: 1px solid var(--hairline);
                        vertical-align: middle; font-variant-numeric: tabular-nums; }
.shipment-timeline col.col-store { width: 13rem; }
.shipment-timeline col.col-date { width: 7rem; }
.shipment-timeline col.col-days { width: 3.5rem; }
.shipment-timeline .numeric { text-align: right; }

/* The store column is pinned while the chart scrolls under it, and carries the
   health spine the schedule puts on the same column -- the two screens agree
   about what "behind" looks like. */
.shipment-timeline th:first-child { position: sticky; left: 0; z-index: 1; }
.shipment-timeline tbody th { font-weight: 500; padding: 0.45rem 0.6rem;
                              border-top: 1px solid var(--hairline); border-bottom: none;
                              color: var(--ink); font-size: 0.875rem; vertical-align: middle;
                              box-shadow: 1px 0 0 var(--hairline); }
.shipment-timeline tbody tr.health-at_risk th { box-shadow: inset 3px 0 var(--at-risk), 1px 0 0 var(--hairline); }
.shipment-timeline tbody tr.health-late    th { box-shadow: inset 3px 0 var(--late),    1px 0 0 var(--hairline); }
.shipment-timeline tbody th a { color: var(--ink); text-decoration: none; }
.shipment-timeline tbody th a:hover { color: var(--teal); text-decoration: underline; }
.shipment-timeline tbody tr:hover td, .shipment-timeline tbody tr:hover th { background: var(--panel); }

/* The day the goods actually landed, where that is not the day the store was
   given. Red only when it landed late: an early arrival is a fact, not a
   problem, and the status red is reserved for what needs somebody. */
.shipment-timeline .arrived { display: block; color: var(--slate); font-size: 0.78rem; }
.shipment-timeline .arrived.is-slipped { color: var(--late); }

/* The axis. Tick labels sit along the bottom, the today label along the top,
   so the two cannot overwrite each other. */
.shipment-timeline th.axis-head { padding: 0 0 0.4rem; }
.shipment-timeline .axis { position: relative; display: block; height: 2.2rem; }
.shipment-timeline .tick { position: absolute; top: 0.9rem; bottom: 0; width: 1px; background: var(--mist); }
.shipment-timeline .tick-label { position: absolute; left: 0.25rem; bottom: -0.15rem;
                                 white-space: nowrap; font-size: 0.72rem; color: var(--slate); }

/* Today is structure, not status: it is where the reader is standing, so it
   takes the ink rather than borrowing the red, amber and green that mean a
   shipment needs somebody. */
.shipment-timeline .today-mark { position: absolute; top: 0.6rem; bottom: 0; width: 2px; background: var(--ink); }
.shipment-timeline .today-label { position: absolute; left: 0.3rem; top: -0.75rem; white-space: nowrap;
                                  font-size: 0.72rem; font-weight: 600; color: var(--ink); }
.shipment-timeline .today-mark.is-near-end .today-label { left: auto; right: 0.3rem; }

/* The chart column. The track strip is centred in a cell tall enough to leave
   air around it, while the gridlines and the today rule run the full height of
   the cell -- table cells touch, so they join into one continuous rule down
   the chart. */
.shipment-timeline td.track { position: relative; padding: 0; height: 1.9rem; }
.shipment-timeline td.track::before { content: ""; position: absolute; left: 0; right: 0; top: 50%;
                                      transform: translateY(-50%); height: 0.85rem; background: var(--teal-wash); }
.shipment-timeline .gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--hairline); }
.shipment-timeline .today-rule { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--ink); }
.shipment-timeline .span { position: absolute; top: 50%; transform: translateY(-50%);
                           height: 0.85rem; background: var(--teal); }
.shipment-timeline .span.health-at_risk { background: var(--at-risk-field); }
.shipment-timeline .span.health-late { background: var(--late); }

/* ------------------------------------------------------------------ *
 * Notices and refusals. The alert borrows the status red for the same reason
 * a form error does -- it is the "something is wrong" signal the rest of the
 * product uses it for, not decoration.
 * ------------------------------------------------------------------ */
.flashes { margin-bottom: 1.25rem; }
.flash { margin: 0 0 0.5rem; padding: 0.55rem 0.8rem; border-left: 3px solid var(--mist);
         background: var(--panel); max-width: 60rem; }
.flash-notice { border-left-color: var(--on-time); }
.flash-alert { border-left-color: var(--late); }
/* The only animation in the application, and the only thing it does is show
   what arrived in response to something the reader just did. A console is not
   a landing page. */
@media (prefers-reduced-motion: no-preference) {
  .flash { animation: flash-in 180ms ease-out; }
  @keyframes flash-in { from { opacity: 0; transform: translateY(-0.25rem); } }
}

/* ------------------------------------------------------------------ *
 * Sign in, forgotten password and set-a-new-password. The first screen
 * anybody sees, so it gets the brand as a solid field rather than as a
 * link colour, and none of the <br>-and-inline-style scaffolding it
 * shipped with.
 * ------------------------------------------------------------------ */
.auth { max-width: 26rem; margin: 3.5rem auto; border: 1px solid var(--hairline); }
.auth-masthead { background: var(--teal); color: var(--paper); padding: 1.5rem 1.75rem; }
.auth-masthead h1 { color: var(--paper); font-size: 1.6rem; margin: 0 0 0.4rem; }
.auth-masthead .auth-intro { color: var(--teal-ink); margin: 0; font-size: 0.9rem; }
.auth-body { padding: 1.5rem 1.75rem; }
.auth-body .auth-aside { margin: 1.25rem 0 0; font-size: 0.9rem; }
.auth-body input[type=submit] { width: 100%; }

/* Stacked label-over-control forms: sign in, and the admin forms. */
.stacked p { margin: 0 0 1rem; }
.stacked label { display: block; font-size: 0.85rem; font-weight: 500; color: var(--slate); margin-bottom: 0.25rem; }
.stacked input[type=email], .stacked input[type=password], .stacked input[type=text] { width: 100%; box-sizing: border-box; }

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

/* Empty states, across every screen that has one -- the schedule's buckets,
   the dashboard, the calendar, the timeline and now admin. One rule rather
   than restyling each page that happens to use the convention. */
.empty { color: var(--slate); max-width: 60ch; }

/* Field help under a form control: what the setting does, stated once, in
   the reader's words -- not a repeat of the label. */
/* 60ch is the same measure .empty uses. Help text is the one thing on an
   admin screen long enough to run the full width of a 1280px window, and a
   sentence that wide is a sentence nobody finishes. */
.help { display: block; color: var(--slate); font-size: 0.85em; margin: 0.3rem 0 0;
        line-height: 1.45; max-width: 60ch; }

/* Validation errors. The left border borrows the status red deliberately --
   a form error is the same kind of "something is wrong" signal the schedule
   uses it for elsewhere, not brand decoration. */
.errors { border-left: 3px solid var(--late); padding: 0.5rem 0.9rem; margin-bottom: 1rem;
          background: var(--panel); max-width: 60rem; }
.errors p { margin: 0.2rem 0; }

/* The same message again, under the control it belongs to -- the summary above
   says something is wrong, this says which field. Red for the same reason, and
   not red alone: the sentence names the field. */
.field-error { display: block; color: var(--late); font-weight: 500; font-size: 0.85em; margin: 0.3rem 0 0; }
.field_with_errors input, .field_with_errors select, .field_with_errors textarea { border-color: var(--late); }

/* The strip across the top of every admin screen. Set like the nav links it
   sits under -- the same measure, the same teal on hover -- and the page you
   are on is marked with the brand rather than with a heavier weight, because
   a bolder word in a row of words reflows the row as you move between them. */
.subnav { display: flex; gap: 1.25rem; flex-wrap: wrap; margin: 0 0 1.25rem;
          padding-bottom: 0.5rem; border-bottom: 1px solid var(--hairline); }
.subnav a { font-size: 0.9rem; color: var(--slate); text-decoration: none;
            padding: 0.15rem 0; border-bottom: 2px solid transparent; }
.subnav a:hover { color: var(--teal); border-bottom-color: var(--teal); }
.subnav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--teal); }

/* Admin: plain lined listings for customers and people -- the same table
   language as the schedule, under its own name since these rows carry no
   health state. */
.listing { width: 100%; border-collapse: collapse; font-size: 0.9rem; max-width: 70rem; }
.listing th { text-align: left; font-weight: 500; color: var(--slate); padding: 0.3rem 0.6rem 0.4rem;
              font-size: 0.8rem; border-bottom: 1px solid var(--ink); }
.listing td { padding: 0.5rem 0.6rem; border-top: 1px solid var(--hairline); vertical-align: top; }
.listing .muted { color: var(--slate); }
/* A sortable heading. Set as the heading it is rather than as a link, because
   a row of underlined blue words across the top of a table reads as
   navigation; the arrow is what says it is sortable, and it appears on the
   column that is. */
.listing th .sorter { color: inherit; text-decoration: none; display: inline-flex; gap: 0.3rem; }
.listing th .sorter:hover { color: var(--ink); }
.listing th .sorter-arrow { color: var(--teal); }
/* Why a button is missing. An administrator told only "no" -- or shown only an
   absence -- goes looking for a reason, so the reason is on the row. Narrow,
   because it sits in the actions column beside one-word buttons. */
.listing .why { display: block; max-width: 44ch; font-size: 0.85em; line-height: 1.45; }
/* The second line in the Who column of the activity page: who was standing in
   this account's shoes. On its own line and in the secondary colour, because
   the account is what the rest of the row is about and this is the
   qualification on it -- a historical fact, not something to act on, so no
   status colour. */
.listing .stood-in { display: block; color: var(--slate); font-size: 0.85em; }

/* Admin row actions: an Edit link beside two or three button_to forms. They
   are POSTs rather than links because each one changes state, and they are
   set as links because a row carrying three solid teal buttons would shout
   the rarest actions on the page louder than anything on it. Same reasoning
   as the saturation rule in AGENTS.md. */
.listing .row-actions { display: flex; gap: 0.9rem; align-items: baseline; flex-wrap: wrap; }
.listing .row-actions form { display: inline; margin: 0; }
.listing .row-actions input[type=submit], .listing .row-actions button[type=submit] {
  background: none; border: none; padding: 0; height: auto; font-weight: 400;
  color: var(--teal); text-decoration: underline; cursor: pointer;
}
.listing .row-actions input[type=submit]:hover,
.listing .row-actions button[type=submit]:hover { background: none; color: var(--ink); }

/* ------------------------------------------------------------------ *
 * Narrow viewports. The board stacks and its three tiles become one strip;
 * the schedule keeps its table and scrolls sideways with the store column
 * pinned, because comparing a date down a column is the whole job and a
 * stack of cards cannot do it.
 * ------------------------------------------------------------------ */
@media (max-width: 62rem) {
  /* The board stacks and the three tiles become one strip across the top,
     under the verdict: at this width a column of three figures beside a column
     of four panels is a column of whitespace. */
  .board { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .board-lede      { grid-column: 1 / -1; grid-row: 1; min-height: 0; padding: 1.5rem; }
  .tile-late       { grid-column: 1;      grid-row: 2; }
  .tile-at_risk    { grid-column: 2;      grid-row: 2; }
  .tile-this_week  { grid-column: 3;      grid-row: 2; }
  .board-queue     { grid-column: 1 / -1; grid-row: 3; }
  .board-behind    { grid-column: 1 / -1; grid-row: 4; }
  .board-ahead     { grid-column: 1 / -1; grid-row: 5; }
  .board-churn     { grid-column: 1 / -1; grid-row: 6; }
  .ahead { max-width: 34rem; }
  .board-without-queue .board-behind { grid-column: 1 / -1; }
  .tile { min-height: 7rem; padding: 1rem; }
  .tile .figure { font-size: 2.4rem; }
  .field { grid-template-columns: minmax(0, 1fr); gap: 0.25rem; }
  .field dt { padding-top: 0; }
}

@media (max-width: 40rem) {
  .page { padding: 1rem; }
  .chrome { padding: 0.6rem 1rem; gap: 0.75rem; }
  .chrome .nav-links { padding-left: 0; border-left: none; gap: 1rem; order: 3; width: 100%; }
  .chrome .needs-you { margin-left: auto; }
  .calendar td { height: 3.5rem; font-size: 0.75rem; }
  .board-panel { padding: 1rem; }
  /* Four columns of counts on a phone. The headings are what runs out of room
     first, so the whole table steps down a size rather than wrapping
     "Deliveries" over two lines in a cell two digits wide. */
  .ahead th, .ahead td { padding: 0.35rem 0.4rem; font-size: 0.8rem; }
  .ahead thead th { font-size: 0.7rem; }

  /* The timeline gives back the width the chart can spare rather than the
     width the dates need: the dates are the answer, the chart is the shape. */
  .shipment-timeline { min-width: 46rem; font-size: 0.8rem; }
  .shipment-timeline col.col-store { width: 9.5rem; }
  .shipment-timeline col.col-date { width: 6rem; }
  .shipment-timeline tbody th { font-size: 0.8rem; }
}

/* ------------------------------------------------------------------ *
 * Requirements: what Infinity asked for and how far each of it has got.
 * Read by the client, so it is set like a document rather than like a
 * console -- one big light figure, a definition of what the figure means,
 * and then nine lined entries. No cards, no shadows: the hairline between
 * two entries is the only structure a lined page needs.
 * ------------------------------------------------------------------ */
.req-lede { display: grid; grid-template-columns: 11rem minmax(0, 34rem); gap: 2rem;
            align-items: start; margin: 0 0 1.5rem; }
.req-overall { margin: 0; }
/* Display type: the same light, large, tightened setting the page headings
   use, because this figure is the headline of the page. */
.req-overall .figure { display: block; font-weight: 200; font-size: 3.4rem; line-height: 1;
                       letter-spacing: -0.03em; color: var(--ink); }
.req-overall-note { display: block; color: var(--slate); font-size: 0.85rem; line-height: 1.45;
                    margin-top: 0.5rem; }
.req-definition { margin: 0; line-height: 1.6; font-size: 0.95rem; }

/* The one thing on this page that needs somebody, so it is the one solid
   field on it. Same rule as the dashboard tiles and the nav badge. */
.req-standing { background: var(--teal); color: var(--paper); padding: 0.7rem 1rem;
                margin: 0 0 2rem; max-width: 47rem; font-size: 0.95rem; line-height: 1.5; }
.req-standing strong { color: var(--paper); font-weight: 600; }

.req-heading { margin: 2rem 0 0.25rem; color: var(--slate); font-size: 0.95rem; font-weight: 500; }
.req-list { list-style: none; padding: 0; margin: 0; max-width: 47rem; }
/* One measure for all three blocks in an entry -- the quote, what is built
   and the ask -- so their edges line up instead of making three ragged
   columns of one paragraph each. */
.req-item { display: grid; grid-template-columns: 2.5rem minmax(0, 34rem) 8rem; gap: 1.25rem;
            padding: 1.1rem 0; border-top: 1px solid var(--hairline); align-items: start; }
.req-number { color: var(--slate); font-size: 0.95rem; padding-top: 0.15rem; }
.req-body h3 { margin: 0 0 0.35rem; font-size: 1.05rem; font-weight: 500; }

/* What Infinity asked for, in their words, set apart from what we say about
   it by a camel rule -- decoration, which is all the camel may carry. */
.req-asked { margin: 0 0 0.6rem; padding-left: 0.75rem; border-left: 2px solid var(--camel);
             color: var(--slate); font-size: 0.9rem; line-height: 1.55; }
.req-done { margin: 0; font-size: 0.9rem; line-height: 1.6; }

/* The ask. Quiet enough to repeat seven times down a page, loud enough that
   nobody scrolls past it: the brand as a rule and a wash rather than as a
   solid field, which is kept for the standing line at the top. */
.req-needs { margin: 0.75rem 0 0; border-left: 3px solid var(--teal); background: var(--teal-wash);
             padding: 0.6rem 0.9rem; }
.req-needs h4 { margin: 0 0 0.25rem; font-size: 0.78rem; font-weight: 600; color: var(--teal); }
.req-needs p { margin: 0; font-size: 0.9rem; line-height: 1.6; }

.req-measure { text-align: right; }
.req-percent { display: block; font-family: var(--mono); font-size: 1.3rem; font-weight: 400;
               font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
/* A gauge rather than a gradient: the frame is the whole of the requirement
   and the fill is the part of it that is built, with the quarters ruled off
   underneath so the fill is read against a scale instead of being a coloured
   strip that happens to be some length. */
.req-bar { display: block; position: relative; height: 0.5rem; margin: 0.4rem 0 0.75rem;
           border: 1px solid var(--mist); background: var(--paper); }
.req-bar-fill { position: absolute; top: 0; bottom: 0; left: 0; background: var(--teal); }
.req-bar::after { content: ""; position: absolute; left: 0; right: 0; top: calc(100% + 1px);
                  height: 0.22rem;
                  background-image: repeating-linear-gradient(to right, var(--mist) 0 1px, transparent 1px 25%); }
.req-status { display: block; font-size: 0.78rem; color: var(--slate); }
.req-item.is-complete .req-status { color: var(--ink); font-weight: 500; }
.req-item.is-waiting .req-status { color: var(--teal); font-weight: 600; }
.req-source { margin: 2rem 0 0; padding-top: 1rem; border-top: 1px solid var(--hairline);
              color: var(--slate); font-size: 0.85rem; max-width: 47rem; }

@media (max-width: 52rem) {
  .req-lede { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .req-item { grid-template-columns: 2rem minmax(0, 1fr); gap: 0.75rem; }
  /* The measure moves under the entry it measures rather than squeezing a
     third column into a phone, and reads left to right like everything else
     on the row. */
  .req-measure { grid-column: 2; text-align: left; display: flex; align-items: center;
                 gap: 0.75rem; margin-top: 0.75rem; }
  .req-bar { flex: 1 1 auto; margin: 0; max-width: 12rem; }
  .req-percent { font-size: 1.05rem; }
}

/* Text for a screen reader where the screen already says it in another way --
   the name of a column whose contents are a picture. */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
                   overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
