/* 03-layout.css: Layout primitives: page header, busy indicator, button variants, step tabs, wizard track,
   section heading, twelve-column grids and spans, fields.
   Loaded by base.html, one <link> per file in number order; a later file wins on source order. */

/* ===========================================================================
   Layout primitives.

   Derived from docs/mockups/HEA-232 (candidate create), HEA-191 (candidate
   list), HEA-192 (candidate detail), HEA-194 (contract create) and HEA-185
   (supplier create, for the step tabs). Server-rendered screens compose these
   instead of leaning on Django's default form rendering: a screen that emits
   {{ form.as_p }} has no layout at all, which is what these replace.

   Companion partials live in backend/templates/components/.
   =========================================================================== */

/* Screen title + optional meta line on the left, action cluster on the right.
   The action buttons sit in the header (not under the form) per the mockups,
   so the submit button carries form="<id>" to reach the form below it. */
.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.5rem;
  margin-bottom: 1.75rem;
}

/* A title with no break opportunity (a sent mail's long subject) wraps inside the header instead
   of pushing the actions off the page: the title block may shrink, and the title may break
   anywhere. */
.page-header > :first-child {
  min-width: 0;
}

/* A header whose actions drop under the title on a screen too narrow for both. */
.page-header--wrap {
  flex-wrap: wrap;
}

.page-header h1 {
  margin: 0;
  font-size: 1.75rem;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

/* The "458 kandidaten - status is afgeleid uit de contracten" line under a
   list title. */
.page-header__meta {
  margin: 0.4rem 0 0;
  color: var(--color-muted);
  font-size: 0.875rem;
}

.page-header__actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
}

/* The same Cancel/Save cluster repeated under a long form, so the reader who
   just finished the last section does not scroll back up to save. */
.form-footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 1.5rem;
}

/* A link-button (Annuleren, Vorige) inherits body's font and 1.6 line-height,
   a <button> gets the UA's own: without one shared line-height the links
   sit 3-6px taller than the buttons beside them. .btn/.btn-quiet set this on
   themselves (app-wide, not just here); this covers .btn-dark and any
   unclassed <button>/<a> that still lands in a header. A POST action's
   button sits one level down, in its .inline-form. */
.page-header__actions > a,
.page-header__actions > button,
.page-header__actions > .inline-form > button {
  line-height: 1.25;
}

/* Busy indicator (HEA-407 follow-up), one convention app-wide. A button whose
   request is running carries aria-busy="true" and draws a spinner before its
   label: server forms get it from static/busy.js on submit (with the label
   swapped for data-busy-text), htmx from its htmx-request class, React islands
   by setting aria-busy themselves (any button, link-styled ones included).
   .spinner is the same circle on its own, for a line of text ("vertalingen
   worden voorgesteld"). */
.spinner,
button[aria-busy="true"]::before,
.btn.htmx-request::before,
.btn-quiet.htmx-request::before {
  content: "";
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  vertical-align: -0.1em;
  animation: spinner-turn 0.7s linear infinite;
}

button[aria-busy="true"]::before,
.btn.htmx-request::before,
.btn-quiet.htmx-request::before {
  margin-right: 0.5em;
}

.spinner {
  margin-right: 0.4em;
}

button[aria-busy="true"],
.btn.htmx-request,
.btn-quiet.htmx-request {
  cursor: progress;
}

@keyframes spinner-turn {
  to {
    transform: rotate(360deg);
  }
}

/* Without motion the circle stands still: the ring with its gap still reads as "working". */
@media (prefers-reduced-motion: reduce) {
  .spinner,
  button[aria-busy="true"]::before,
  .btn.htmx-request::before,
  .btn-quiet.htmx-request::before {
    animation: none;
  }
}

/* Secondary action (Annuleren / Terug): light border, unlike .btn-outline's
   heavy dark one. */
.btn-quiet {
  display: inline-block;
  box-sizing: border-box;
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border-strong);
  /* .btn-quiet's border is 1px to .btn's 2px; the extra pixel of padding
     keeps it the same height as a neighbouring .btn without a heavier border. */
  padding-block: calc(0.56rem + 1px);
  padding-inline: 1.1rem;
  border-radius: var(--radius-button);
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  /* See .btn's line-height comment: same fix, so an <a class="btn-quiet"> and
     a <button class="btn-quiet"> (e.g. a per-row Opnieuw / Intrekken pair)
     match height too. */
  line-height: 1.25;
  text-decoration: none;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
}

.btn-quiet:hover {
  border-color: var(--color-text);
}

/* .btn-sm is declared before .btn-quiet, so on its own it loses the padding
   and size to it; a small quiet button (a per-row Intrekken) needs both. */
.btn-quiet.btn-sm {
  padding: 0.2rem 0.6rem;
  font-size: 0.8rem;
}

/* An action that is present but not available here yet (HEA-186's document
   "+", which works on the dossier once the supplier exists). */
.btn-quiet:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-quiet:disabled:hover {
  border-color: var(--color-border-strong);
}

/* Wizard navigation (Vorige / Volgende, a pair, so they look alike) and
   inline fetch actions ("Gegevens ophalen"): black -- the secondary level,
   between .btn-quiet's way out and .btn's primary action. See the button
   hierarchy in docs/mockups/README.md. */
.btn-dark {
  display: inline-block;
  background: var(--color-surface-strong);
  color: #fff;
  border: 2px solid var(--color-surface-strong);
  padding: 0.56rem 0.95rem;
  border-radius: var(--radius-button);
  font-size: 0.875rem;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
}

.btn-dark:hover {
  background: var(--color-text);
}

/* A destructive action ("Vervallen verklaren"): the quiet button's weight in the error colour,
   so it never outweighs the primary action beside it. */
.btn-danger {
  display: inline-block;
  box-sizing: border-box;
  background: var(--color-surface);
  color: var(--color-error);
  border: 1px solid var(--color-error);
  padding-block: calc(0.56rem + 1px);
  padding-inline: 1.1rem;
  border-radius: var(--radius-button);
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.25;
  text-decoration: none;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
}

.btn-danger:hover {
  background: var(--color-error-bg);
}

/* The actions under a form, on one row: primary first, the rest beside it, wrapping when
   narrow. */
.actions-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1rem;
}

/* Several checkboxes of one field on a row (an article's contract types). */
.checkbox-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.5rem;
}

.checkbox-row label {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.875rem;
}

/* Read-only text in a field cell (an article version's NL and EN side by side). */
.field__text {
  margin: 0;
  font-size: 0.875rem;
  white-space: normal;
}

/* An action whose integration is not built yet ("Gegevens ophalen",
   HEA-187): visibly present, visibly inert. */
.btn-dark:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-dark:disabled:hover {
  background: var(--color-surface-strong);
}

/* Numbered step tabs on multi-step forms ("1 - Bedrijf & financieel"). */
.wizard-steps {
  display: flex;
  gap: 0.5rem;
  margin: 0 0 1.5rem;
}

.wizard-step {
  padding: 0.4rem 0.9rem;
  border-radius: var(--radius);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-muted);
  text-decoration: none;
}

/* Tab fills sampled from HEA-185's mockups: the step tabs are duskier than
   the badge tokens (primary-soft / success-bg), so they carry their own. */
.wizard-step--active {
  background: #f0ddd7;
  color: var(--color-primary-hover);
}

/* Step tabs under a page title (HEA-185): the header already spaces the
   page below it, so the tabs only need a gap from the title above. */
.page-header .wizard-steps {
  margin: 0.6rem 0 0;
}

/* A step already saved: green, with a check after its label (HEA-185). */
.wizard-step--done {
  background: #e9efe7;
  color: var(--color-success-fg);
}

a.wizard-step:hover {
  text-decoration: underline;
}

/* Numbered step track for long wizards (the template wizard, HEA-204/HEA-407): one circle per
   step on a line, the label under it. Measured on docs/mockups/HEA-204_wizard_2_artikelen.png at
   1440: the steps take equal columns across the full content band (40-1400, circle centres 194px
   apart), the 2px line runs from the first circle's centre to the last, circles are 27px, labels
   12px directly under them. The colours are sampled from the PNG: done #3f7a48, the line #e6e0d3,
   an upcoming circle's 2px ring #d8d1c2 and its label #8d8778 -- all lighter than the nearest
   tokens, which read too heavy on a track that sits under every wizard title. The current step is
   the primary colour with its label in ink. Set --wizard-track-steps on the element when a wizard
   has a different number of steps; the shorter two-step forms keep .wizard-steps. */
.wizard-track {
  --wizard-track-steps: 7;
  --wizard-track-dot: 27px;
  margin: 0.5rem 0 0;
}

.wizard-track__list {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--wizard-track-steps), minmax(0, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}

.wizard-track__list::before {
  content: "";
  position: absolute;
  top: calc(var(--wizard-track-dot) / 2 - 1px);
  left: calc(50% / var(--wizard-track-steps));
  right: calc(50% / var(--wizard-track-steps));
  height: 2px;
  background: #e6e0d3;
}

.wizard-track__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: #8d8778;
  font-family: inherit;
  font-size: 0.75rem;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
}

.wizard-track__dot {
  position: relative;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: var(--wizard-track-dot);
  height: var(--wizard-track-dot);
  border: 2px solid #d8d1c2;
  border-radius: 50%;
  background: var(--color-surface);
  font-size: 0.6875rem;
  font-weight: 700;
}

.wizard-track__step--done .wizard-track__dot {
  border-color: #3f7a48;
  background: #3f7a48;
  color: #ffffff;
}

.wizard-track__step--current .wizard-track__dot {
  border-color: var(--color-primary);
  background: var(--color-primary);
  color: #ffffff;
}

.wizard-track__step--current .wizard-track__label {
  color: var(--color-text);
  font-weight: 700;
}

/* A step with open items: amber on the warning tokens, the number kept in the circle. Shared by
   the template wizard island and the contract flow (HEA-463). */
.wizard-track__step--open .wizard-track__dot {
  border-color: var(--color-warning-fg);
  background: var(--color-warning-bg);
  color: var(--color-warning-fg);
}

.wizard-track__step--open .wizard-track__label {
  color: var(--color-warning-fg);
  font-weight: 600;
}

.wizard-track__link:hover .wizard-track__label {
  text-decoration: underline;
}

/* A step that cannot be reached yet, or the current one: the same look, no link behaviour. */
.wizard-track__link--static {
  cursor: default;
}

.wizard-track__link--static:hover .wizard-track__label {
  text-decoration: none;
}

.wizard-track__link:focus-visible {
  outline: none;
}

.wizard-track__link:focus-visible .wizard-track__dot {
  outline: 3px solid rgba(230, 83, 46, 0.35);
  outline-offset: 2px;
}

/* Section heading with its explanatory note on the same baseline. */
.form-section__head {
  display: flex;
  align-items: baseline;
  gap: 1.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.form-section__head h2 {
  margin: 0;
}

/* A heading too long to share its line with the note: the note sits right under it, instead of
   wrapping onto a second flex row a full gap below. */
.form-section__head--stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25rem;
}

/* A card's h2 and its paragraphs carry bottom margins of their own; in a stacked head the gap
   spaces title and note, and the head's own margin spaces it from the card, so both go. */
.form-section .form-section__head--stacked > * {
  margin: 0;
}

.section-note {
  margin: 0;
  color: var(--color-muted);
  font-size: 0.8125rem;
}

/* Twelve-column grids. Both the field grid (fields inside a card) and the
   section grid (whole cards on a page) use the same .span-N modifiers, so
   there is one column vocabulary to learn rather than two.

   Density rule, from the mockups: fill every row. HEA-232's second row is
   postcode(2) + huisnr.(2) + geboortedatum(4) + initialen(4); it does not
   leave half a row empty, and it does not spend a card on the address. */
.field-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 0.9rem 1.5rem;
}

/* A grid of read-only rows rather than labelled inputs (HEA-186's insurance
   and document lists): each row's cells sit on one centre line. */
.field-grid--center {
  align-items: center;
}

/* A note in such a row (components/_yes_no.html) centres on the line too: without this the page's
   paragraph margin makes its box taller than its text, and the text sits above the toggle. */
.field-grid--center > .section-note {
  margin: 0;
}

/* A cell whose content sits at the row's right edge (a row's action button). */
.field-grid__end {
  display: flex;
  justify-content: flex-end;
}

/* Cards side by side instead of stacked. HEA-192 sets a narrow "Gegevens"
   card next to a wide "Documenten & checks" card: .span-4 + .span-8.

   Cards in a row share a height (the grid default) rather than each ending
   at its own content: a short card beside a tall one leaves a ragged edge
   that reads as a rendering fault rather than a layout. */
.section-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 1.25rem;
  margin-top: 1.25rem;
}

/* Blocks are full width unless given a span; the card's own top margin would
   otherwise fight the grid gap and misalign the row. */
.section-grid > * {
  grid-column: span 12;
  margin-top: 0;
}

/* A card that stands as tall as its content instead of stretching to match
   the tallest card in its row (HEA-447). Grid items stretch by default, which keeps two
   cards of similar length flush but leaves a large empty box when one is much
   shorter, as the candidate detail's document card is for a role that sees
   the checks but not the documents. */
.form-section--fit {
  align-self: start;
}

.field {
  grid-column: span 4;
  min-width: 0;
}

/* All twelve spans exist so a template can never silently fall back to the
   default width by naming a span that has no rule. Declared after .field and
   .section-grid > * so they win on source order at equal specificity. */
.span-1 {
  grid-column: span 1;
}

.span-2 {
  grid-column: span 2;
}

.span-3 {
  grid-column: span 3;
}

.span-4 {
  grid-column: span 4;
}

.span-5 {
  grid-column: span 5;
}

.span-6 {
  grid-column: span 6;
}

.span-7 {
  grid-column: span 7;
}

.span-8 {
  grid-column: span 8;
}

.span-9 {
  grid-column: span 9;
}

.span-10 {
  grid-column: span 10;
}

.span-11 {
  grid-column: span 11;
}

.span-12 {
  grid-column: span 12;
}

.field__label {
  display: block;
  margin-bottom: 0.25rem;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-muted);
}

.field__required {
  color: var(--color-primary);
  margin-left: 0.15rem;
}

/* A pill inside a label (the "nog niet geïmplementeerd" badge) sits within the label's own line, so
   its row is as tall as the labels beside it and their controls share one top. */
.field__label .badge {
  padding-block: 0;
  font-size: 0.7rem;
  line-height: 1.25;
}

/* Controls fill their grid cell; the global 28rem cap below is for the
   not-yet-retrofitted screens and must not apply inside a grid.

   The tighter padding/font are scoped to .field rather than changed on the
   global input selector, so screens that have not been retrofitted yet keep
   rendering exactly as they do today. The mockups' inputs are ~32px tall;
   the global rule's 0.6rem/0.9rem gives ~42px, which is most of the reason
   a form that fits on one screen in the mockup would not fit in ours.

   The [type] on the input selector is load-bearing. The global rule below is
   input[type="text"] etc., which is (0,1,1) and declared later in the file, so
   a plain ".field input" ties on specificity and loses on source order -- its
   max-width:none would never apply and every field would be silently capped
   at 28rem inside a grid cell that is bigger. ".field input[type]" is
   (0,2,1) and wins outright. */
.field input[type],
.field select,
.field textarea {
  width: 100%;
  max-width: none;
  padding: 0.4rem 0.7rem;
  font-size: 0.875rem;
}

/* One control height per row. At the padding above a select and a date input draw 36px and a text
   input 34px, so a row that mixes them (step 1 of the contract flow, HEA-463) misses by 2px. */
.field input[type]:not([type="checkbox"], [type="radio"], [type="file"]),
.field select {
  min-height: 2.25rem;
}

/* A .segmented toggle inside a .field (components/_yes_no_field.html): its radios stay the clipped
   1px inputs .segmented draws, not the full-width control the rule above makes of an input. */
.field .segmented__option input[type] {
  width: 1px;
  min-height: 0;
  padding: 0;
}

/* ...except a checkbox, which the rule above stretched to the cell's width
   and so centred in it, away from its label (HEA-189's "Lever ik later aan"). */
.field input[type="checkbox"] {
  width: auto;
}

/* ...and a file input, which draws no box of its own: the side padding above
   pushed its "Choose file" button ~11px off the label's and the other fields'
   left edge. The vertical padding stays, so its row keeps the fields' height. */
.field input[type="file"] {
  padding-left: 0;
  padding-right: 0;
}

/* The UA's native checkbox fill is the browser blue, which is not in the
   token set (the New contract screen's "Create a new customer-broker
   relation", a table's row selection). accent-color recolours the native control without replacing
   it, so it stays a checkbox for the keyboard and the screen reader; the
   primary is this palette's affirmative action colour -- green stays
   reserved for "done" states (.wizard-step--done). */
.field input[type="checkbox"],
.check-inline input,
.form-section__head input[type="checkbox"],
.data-table input[type="checkbox"] {
  accent-color: var(--color-primary);
}

/* Django's default Textarea is rows=10, far taller than the mockups' notes
   fields; height wins over the rows attribute and users can still drag. */
.field textarea {
  height: 4.5rem;
  min-height: 3rem;
  resize: vertical;
}

/* ...or one control high, beside inputs in a row, for text that may still hold line breaks
   (step 1's Instructie urenaanlevering, HEA-463). */
.field textarea.textarea--one-line {
  height: 2.25rem;
  min-height: 2.25rem;
}

/* ...except where the text is the whole point, e.g. a mail body edited before
   sending: tall enough to read the mail without dragging. */
.field-grid--long-text .field textarea {
  height: 22rem;
}

/* ...and an article's NL/EN clause text: about eight lines, room for a clause without dragging. */
.field-grid--clause-text .field textarea {
  height: 12rem;
}

/* Derived / read-only values (facturatiepartij, contractnummer) sit on the
   darker cream fill in the mockups. */
/* The [type] on the input selector is load-bearing for the same reason as in
   .field above: a plain ".field--readonly input" is (0,1,1), ties with the
   global input[type="text"] rule, and loses on source order -- so the derived
   fill would silently never reach a text or date input, only selects and
   textareas. ".field--readonly input[type]" is (0,2,1) and wins. */
.field--readonly input[type],
.field--readonly select,
.field--readonly textarea,
.field input[type]:disabled,
.field select:disabled,
.field textarea:disabled {
  background: var(--color-bg-accent);
  color: var(--color-muted);
  /* The UA fades a disabled select further than an input; one fill for both. */
  opacity: 1;
}

/* Control with a trailing inline action, e.g. KvK-nummer + "Gegevens ophalen". */
.field__control-row {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
  /* Anchors an .sr-only control placed in the row (HEA-180's PDF upload
     input): unanchored, its static position lands 1000px past the page
     edge and makes the page scroll horizontally. */
  position: relative;
}

.field__control-row > .sr-only {
  left: 0;
  top: 0;
}

.field__control-row > input,
.field__control-row > select {
  flex: 1 1 auto;
  min-width: 0;
}

/* The trailing action keeps its label on one line; a wrapped label makes the
   button, and the input stretched beside it, two lines tall. */
.field__control-row > button,
.field__control-row > .btn-dark {
  flex: 0 0 auto;
  white-space: nowrap;
  padding-block: 0.25rem;
}

/* A .btn-dark <label> (the PDF upload trigger) has no input of its own to set the row's height: the
   real input is sr-only. Give it the fields' one control height (2.25rem, see above) and centre its
   text, so it sits level with the field beside it. As a <label> it would also take the field
   label's bottom margin and make the row taller than the button. */
.field__control-row > .btn-dark {
  box-sizing: border-box;
  min-height: 2.25rem;
  margin-bottom: 0;
  display: inline-flex;
  align-items: center;
}

/* The chosen file's name beside that trigger sits level with its label, not at the row's top. */
.field__control-row > .doc-checklist__filename {
  align-self: center;
}

.field__help {
  margin: 0.3rem 0 0;
  font-size: 0.75rem;
  color: var(--color-muted);
}

.field__errors {
  margin: 0.3rem 0 0;
  padding: 0;
  list-style: none;
  color: var(--color-error);
  font-size: 0.75rem;
}

/* "This value just changed, and here is why." Sits where .field__help sits,
   but carries weight and the success tone: a standing hint is furniture the
   eye skips, and this line reports something that happened during the
   request the user is looking at. Not an error -- the change is the system
   doing its job -- so it must not read red. */
/* Scoped through .field to outrank `.page form p`, which otherwise puts a
   1rem skirt under this line and splits it from the help text below it --
   the two annotate one field and read as one block. */
.field p.field__notice {
  margin: 0.3rem 0 0;
  font-size: 0.75rem;
  font-weight: 600;
}

/* A change is not automatically an improvement. The supplier whose SNA
   keurmerk lapses gets this same line, and in success green it would read as
   congratulation, so the two directions carry different tones. Neither is
   red: an error is something the reader has to correct, and this is the
   system reporting what it did. */
.field__notice--success {
  color: var(--color-success-fg);
}

.field__notice--warning {
  color: var(--color-warning-fg);
}
