* {
  box-sizing: border-box;
}

.hidden {
  display: none !important;
}

/*
  Insurance, not the mechanism: every element in this app that needs to be hidden gets
  the .hidden class above, never the native `hidden` attribute (see confirmation.js for
  why -- an author rule setting `display` on the same element beats the UA stylesheet's
  `[hidden] { display: none }` regardless of specificity, which is exactly what silently
  broke the confirmation page's CSV link). This rule just means that if a future author
  rule collides with `hidden` again, the attribute still wins rather than losing silently
  a second time.
*/
[hidden] {
  display: none !important;
}

/*
  For an accessible name that has no visible on-screen counterpart -- e.g. the vendor
  modal's search label, where the placeholder already carries the visible instruction.
  Clipped rather than `display: none`/`.hidden`: a screen reader skips a display:none
  element entirely, which would leave the input with no accessible name at all.
*/
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

body {
  margin: 0;
  font-family: Arial, Helvetica, sans-serif;
  background: #f4f6f8;
  color: #1f2933;
}

.form-container {
  min-height: 100vh;
  display: grid;
  place-items: center;
}

.lead-form-card {
  width: min(680px, 100%);
  background: #ffffff;
  border: 1px solid #d9e2ec;
  border-radius: 12px;
  padding: 24px;
}

.lead-form {
  display: grid;
  gap: 14px;
}

.form-field {
  display: grid;
  gap: 6px;
  font-size: 14px;
  color: #334e68;
}

.form-field input {
  border: 1px solid #bcccdc;
  border-radius: 8px;
  padding: 8px;
  font-size: 14px;
}

/*
  Placeholders carry worked examples ("e.g. Jane"), so they have to be legible rather than
  the usual near-invisible grey -- #607890 is 4.58:1 on the white inputs, the lightest
  blue-slate in the palette's hue that still clears WCAG AA. It stays well clear of body
  text (#1f2933, 14.76:1), which is the other half of the job: an example must not read as
  a value someone already typed.

  `opacity: 1` is load-bearing, not tidying. Firefox applies its own reduced opacity to
  placeholders, which would quietly drag the measured contrast back below AA.

  These are examples ON TOP OF the labels, never instead of them. Every field here is
  wrapped in a <label class="form-field"> that supplies its accessible name, so the
  placeholder disappearing on focus costs nothing. Do not remove a <span> label on the
  grounds that the placeholder already says it.
*/
input::placeholder {
  color: #607890;
  opacity: 1;
}

/*
  `98%`, not `98vw`: a vw unit counts the vertical scrollbar, so on any page long enough
  to scroll the container came out ~15px wider than the space it had and the browser
  added a horizontal scrollbar for the 3px of overflow. Harmless-looking until step 2's
  action bar became `position: fixed` -- a fixed bottom sits ABOVE a horizontal
  scrollbar, so the footer hung 15px off the bottom of the window on exactly the narrow
  viewports that scroll. A percentage resolves against the space actually available and
  is identical to the vw form whenever there is no scrollbar.
*/
.container {
  width: min(98%, 2200px);
  margin: 0 auto;
  padding: 24px clamp(8px, 1.5vw, 20px) 40px;
}

.step-panel {
  margin-bottom: 24px;
}

.step-panel-centered {
  display: flex;
  justify-content: center;
}

.step-panel-centered .lead-form-card,
.step-panel-centered .confirmation-card {
  width: min(680px, 100%);
}

/*
  Step 1 is two columns: the introduction on the left, the card on the right. A grid
  rather than a flex row, for the same reason `.content-layout` is one -- the column caps
  are the layout, and `justify-content: center` then centres the PAIR in the container
  (which is up to 2200px wide) instead of letting them drift apart.

  `align-items: start` keeps the card at the top of its track beside the prose; without it
  both tracks stretch to the taller one and the card's border grows to match the text.

  The caps are 480/680, so between about 1024px and 1230px of content the tracks simply
  share what there is (the grid algorithm hands out free space evenly until a track meets
  its cap), and the card reaches its full 680px on a wide screen.
*/
.step-panel-split {
  display: grid;
  grid-template-columns: minmax(0, 480px) minmax(0, 680px);
  gap: 32px;
  justify-content: center;
  align-items: start;
}

/*
  Stacked below 1024px. That is higher than the app's usual 900px breakpoint on purpose:
  at 900px the two tracks come out about 427px each, which is a cramped form beside
  cramped prose rather than a two-column layout. Both tracks become one 680px column, so
  the prose lines up with the card's edges exactly as it does on a phone.
*/
@media (max-width: 1024px) {
  .step-panel-split {
    grid-template-columns: min(680px, 100%);
  }
}

/*
  Prose, so it takes a wider line-height than the UI text around it. Spacing between it
  and the card is the grid's `gap` in both layouts -- it carries no margin of its own. The
  note is italic slate rather than a bordered box: see the comment on .privacy-notice for
  why the box itself stays on step 3.
*/
.step-intro {
  color: #486581;
  font-size: 15px;
  line-height: 1.6;
  text-align: left;
}

.step-intro-title {
  margin: 0 0 12px;
  font-size: 20px;
  font-weight: 700;
  color: #1f2933;
}

.step-intro p {
  margin: 0 0 12px;
}

.step-intro p:last-child {
  margin-bottom: 0;
}

.step-intro-note {
  font-size: 13px;
  font-style: italic;
  color: #627d98;
}

/*
  The UA's 0.67em top margin is dead weight on every h1 here: each one is the first thing
  in its step panel or card, so the space above it is already owned by `.step-nav`'s
  margin-bottom (or the card's padding). Stacking both put 45px between the breadcrumbs
  and "Package Selection" -- about double what that gap reads as needing, and on step 2
  it came straight out of the package pane, which is sized from what is left over.
  (confirmation.html's h1 sets its own margins and is unaffected.)
*/
h1 {
  margin-top: 0;
  margin-bottom: 8px;
}

.lead {
  margin-top: 0;
  color: #52606d;
}

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  margin: 20px 0;
}

.toolbar-spacer {
  flex: 1;
}

.review-actions {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-top: 28px;
}

.confirmation-actions {
  justify-content: center;
}

/*
  The action bar on steps 2 and 3: a real footer, fixed to the bottom of the viewport.

  It used to be `position: sticky; bottom: 0`, and that is NOT the same thing -- sticky
  only ever pulls an element UP into view, it cannot push one down past where it sits in
  flow. So the bar sat immediately below whatever content there was and slid down the
  page as the workspace filled up: measured 479px above the viewport bottom on an empty
  workspace, and only reaching the bottom once a vendor was expanded. `fixed` puts it at
  the bottom unconditionally, which is what "footer" means here.

  Width and horizontal padding mirror `.container` exactly (the same `min(98%, 2200px)`
  and the same clamp), so Back and Next stay on the page's own left and right edges once
  the bar is out of flow. `left/right: 0` plus auto margins is what centres a fixed box,
  and a fixed box's percentages resolve against the same scrollbar-free width the
  container's do -- see the note on .container for why neither may be `vw`.

  The 60px height is DECLARED rather than emergent, for the same reason the site banner's
  is: the space the page reserves below its content has to agree with it, and 12px of
  padding plus whatever a button happens to measure is a number nobody can look up. Every
  `76px` further down this file is this 60px plus a 16px gap -- change one, change both.

  The opaque background is load-bearing, not decoration: without it the package grid
  scrolls visibly through the bar. It is the same #f4f6f8 as `body` on purpose, so the bar
  reads as the page's own floor rather than as a separate surface, and the top rule is
  what separates it from the content above it.
*/
/* Both class names on the selector: `.review-actions` is redefined further down the
   file with its own margin-top and `justify-content`, and would otherwise win on source
   order. */
.review-actions.pinned-actions {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  width: min(98%, 2200px);
  margin: 0 auto;
  height: 60px;
  padding: 0 clamp(8px, 1.5vw, 20px);
  z-index: 5;
  background: #f4f6f8;
  border-top: 1px solid #d9e2ec;
}

/*
  Sits between the two flex spacers, so it centres between Back and Next rather than
  hugging either. It shrinks before the buttons do (`min-width: 0` plus the ellipsis) --
  a squeezed bar should cost you the end of a guid you can still read in the URL, never
  the ability to reach Next.
*/
.pinned-submission-id {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 13px;
}

/*
  Only ever one copy of the submission id on screen: steps 2 and 3 show it in the pinned
  bar above, so the page-foot copy stands down while either is active. app.js sets the
  class; which of the two wins is decided here, because the answer changes with width
  (see the 700px block at the bottom of this file).
*/
body.pinned-actions-active .session-footer {
  display: none;
}

/*
  The bar is out of flow, so the page has to keep its last 76px (the bar's declared 60px
  plus a 16px gap) clear of it -- otherwise the foot of the content sits behind the bar
  with no way to scroll it out. This replaces the `padding-bottom: 0` the sticky bar
  needed: sticky stops sticking at the end of its containing block, so back then any
  trailing padding left the bar detached and resting up the page at maximum scroll.
  A fixed bar has no containing block to fall off, and wants the opposite.
*/
body.pinned-actions-active .container,
body.step-library-pinned .container {
  padding-bottom: 76px;
}

/*
  Step 1 pins the submission id at the bottom of the viewport the same way steps 2 and 3
  do -- but it has no Back/Next to share a bar with, so the page-foot .session-footer
  simply BECOMES the bar rather than standing down in favour of a second copy inside one.
  That is why this step gets its own body class instead of joining `pinned-actions-active`:
  that class means "the id lives in a bar, hide the page-foot copy", which is the opposite
  of what happens here.

  The box below is `.review-actions.pinned-actions` verbatim -- same width, clamp padding,
  height, z-index, ground and top rule -- because it is the same floor, one step earlier.
  Keep the two in step with each other; the 60px is the same declared 60px that every
  `76px` in this file is derived from. The only difference is what sits inside it: one
  centred id rather than a row of controls, hence the flex centring here and the two
  `.toolbar-spacer` elements there.

  app.js only sets the class once there IS an id (a fresh visit has no session yet). An
  empty 60px stripe ruled across the viewport reads as a rendering fault, and steps 2 and
  3 can never show one because their bars always hold buttons.
*/
body.step-library-pinned .session-footer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  width: min(98%, 2200px);
  margin: 0 auto;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 clamp(8px, 1.5vw, 20px);
  z-index: 5;
  background: #f4f6f8;
  border-top: 1px solid #d9e2ec;
}

/*
  Matched to `.pinned-submission-id` so the id reads the same in all three bars, and
  truncated for the same reason: a squeezed bar should cost you the end of a guid you can
  still read in the URL. Note this step's id is NOT dropped below 700px the way the other
  two are -- there it is competing with Back and Next for the width, and here it has the
  whole bar to itself, so there is nothing to push off.
*/
body.step-library-pinned #session-footer {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 13px;
}

/*
  ...and wherever the PAGE is the scroller (either fallback below), the viewport has to
  know its last 76px are covered, or the browser counts a checkbox sitting behind the bar
  as already visible and will not scroll to it when Tab lands there -- which is the
  reported symptom, one mode down. Scroll-padding on the viewport is taken from the root
  element, hence `html`; `:has` scopes it to the steps that have a fixed bar. Inert in
  step 2's app-shell mode below, where the page does not scroll at all -- but step 3 is
  always in this mode, so it is the rule doing the work there, and so is step 1.
*/
html:has(body.pinned-actions-active),
html:has(body.step-library-pinned) {
  scroll-padding-bottom: 76px;
}

/* The bar supplies the space below the panel now; .step-panel's own 24px would only push
   the page's last row further under it. */
body.pinned-actions-active #step-selection,
body.pinned-actions-active #step-review,
body.step-library-pinned #step-library {
  margin-bottom: 0;
}

/*
  Above the stacking breakpoint, step 2 is an app shell: the PAGE does not scroll at all,
  the two panes scroll inside it, and the fixed bar is the floor. Step 3 is deliberately
  NOT one -- it has a contact form to fill in rather than two lists to hunt through, its
  packages card already scrolls inside itself, and its bar works fine over a page that
  scrolls. These rules stay keyed to `step-selection-active`, not `pinned-actions-active`.

  This is what stops the bar covering checkboxes, and it is the half of the fix that is
  not about the bar. While the page could scroll, the pane was 70vh tall wherever it
  happened to start, so its bottom edge sat below the viewport -- measured 79px under the
  bar at top-of-page. That edge is also the pane's scrollport, so tabbing through
  checkboxes only scrolled the pane once focus had already passed BELOW the bar: always a
  row or two too late, which is exactly the reported symptom. Sizing the pane to end
  where the bar begins makes "outside the scrollport" and "hidden behind the bar" the
  same thing, so the browser's own focus scrolling does the right thing and needs no
  scroll-padding fudge to be talked into it.

  The chain is deliberately all flex/grid rather than heights: the only two numbers in it
  are the banner's 81px and the bar's 76px, both declared elsewhere in this file rather
  than guessed at from a screenshot. `min-height: 0` appears three times because flex and
  grid children refuse to shrink below their content without it -- drop any one of them
  and that box grows to its content and pushes the pane off the bottom again.

  901px is the inverse of the 900px stacking breakpoint near the end of this file, where
  the columns stack and the page goes back to being the scroller.

  The 700px height floor is the other bound, and it is not cosmetic: the chrome above the
  pane (banner, breadcrumbs, heading, lead, toolbar) is a fixed 305px, so pinning all of
  it leaves `viewport - 381px` for the packages -- 319px at 700px tall, and only 69px at
  450px. Below the floor the page goes back to scrolling, chrome and all, which is worth
  more on a short window than a bar that never moves. Both fallbacks are the same mode,
  and the scroll-padding rule above is what keeps Tab honest in it.
*/
@media (min-width: 901px) and (min-height: 700px) {
  body.step-selection-active .container {
    height: calc(100vh - 81px);  /* the site banner above it; see .site-banner */
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  body.step-selection-active #step-selection {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
  }

  body.step-selection-active .content-layout {
    flex: 1;
    min-height: 0;
    /* Both panes fill the row rather than sitting at their content height; the base rule
       starts them because the stacked layout below 901px wants the opposite. */
    align-items: stretch;
  }
}

button {
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 8px 14px;
  cursor: pointer;
  font-size: 14px;
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

.primary {
  background: #2e5c8d;
  color: #ffffff;
}

.danger {
  background: #8d312e;
  color: #ffffff;
}

.ghost {
  border-color: #bcccdc;
  background: #ffffff;
  color: #334e68;
}

.muted {
  color: #627d98;
  font-size: 14px;
}

/*
  Body copy for a confirm dialog, sized to sit between .modal-title and .modal-actions
  (neither of which this file gives .modal-card padding for -- each band pads itself).
  Slate rather than the old banner's red text: the dialog is already a red-buttoned
  interruption, so red prose on top of that just shouts twice.
*/
.modal-text {
  margin: 0;
  padding: 16px 20px;
  color: #334e68;
  font-size: 14px;
  line-height: 1.5;
}

/*
  The left column of .content-layout, replacing #vendor-selections in that role now that
  Package Selection's title/lead/toolbar sit above the vendor cards instead of outside the
  grid (see the comment on #step-selection). A flex column so #vendor-selections -- its
  last, `flex: 1` child -- is the one that stretches into whatever height the grid's
  `align-items: stretch` (app-shell breakpoint) or auto sizing (stacked/narrow) hands this
  box, while the title/lead/toolbar above it keep their natural height. `min-height: 0` for
  the same reason #vendor-selections itself needs it below: without it, a flex/grid item
  refuses to shrink below its content's height, which is what let the vendor column's own
  scrollbar work at all.
*/
.selection-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

/*
  ONE scroller for the whole vendor column, rather than one per vendor card. Each
  .package-list used to cap itself at 260px and scroll internally, so expanding a vendor
  gave you a small window with its own scrollbar nested inside the page's. Now the cards
  expand fully and this column is what scrolls -- a single level of nesting instead of two.
  Keyboard-reachable because the column is full of checkboxes and buttons, so tabbing
  scrolls it.

  Its HEIGHT comes from the app-shell flex chain near the top of this file, not from a
  viewport fraction: `flex: 1` here takes whatever .selection-main has left after its own
  title/lead/toolbar, and .selection-main in turn is stretched to the grid row's height by
  `align-items: stretch` on .content-layout. It was `max-height: 70vh`, which left its
  bottom edge wherever the page happened to have scrolled to -- usually below the pinned
  action bar, which is what made tabbing scroll the pane a row or two too late. Filling the
  space between the toolbar and the bar is what keeps "off the bottom of the pane" and
  "behind the bar" the same event. `min-height: 0` is what lets a flex/grid item shrink
  below its content at all. The 900px block resets the scrolling entirely, where the
  columns stack and a bounded pane on a phone would just be a tiny window.
*/
#vendor-selections {
  display: grid;
  gap: 14px;
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /*
    `start`, not the default `stretch`. Now that the pane is sized to the app shell rather
    than to its content, a short workspace leaves free space at the bottom -- and a grid
    with auto rows hands that space to the ROWS, not to the end of the track. Three
    collapsed cards in a tall pane therefore sat in rows ~120px tall apiece, reading as a
    huge gap between vendors that appeared and vanished with how much was expanded. The
    cards keep their natural height and the leftover space stays at the bottom.
  */
  align-content: start;
  /*
    Keeps the cards clear of the scrollbar. Without this the card's right edge -- and so
    the delete segment flush against it -- landed exactly where the scrollbar track
    starts, with a measured 0px gap, which read as the bar tucking under the scrollbar.
    `scrollbar-gutter: stable` reserves the track's space even when the content is short
    enough not to scroll, so the cards keep one width instead of widening by the
    scrollbar's ~15px whenever every vendor happens to be collapsed.
  */
  padding-right: 10px;
  scrollbar-gutter: stable;
}

/* Holds the card alone now -- the remove button moved into the header bar. */
.vendor-card-wrapper {
  display: flex;
  align-items: flex-start;
}

.vendor-card-wrapper .vendor-card {
  flex: 1;
  min-width: 0;
}

/*
  Reads as a colour change at the end of the blue bar rather than a button beside the card.
  Three things make that work, and all three are load-bearing:
    - it is a child of .vendor-card-header (moved there in app.js), so it sits *in* the bar;
    - `align-self: stretch` plus negative margins cancel the header's 10px padding, so the
      red reaches the bar's top, bottom and right edges with no blue showing around it;
    - no border-radius of its own -- .vendor-card's `overflow: hidden` + `border-radius`
      clip the top-right corner to match the card exactly.
  Tall and narrow by design: full bar height, 40px wide.
*/
.remove-vendor-btn {
  flex-shrink: 0;
  align-self: stretch;
  width: 40px;
  padding: 0;
  margin: -10px -10px -10px 0;
  /*
    Grey at rest, red only on hover/focus: destructive, but not shouting from every card.
    #3a424a is the blue's hue at S12%, so it reads as grey beside the bar rather than
    muddy. It is deliberately DARKER than the bar rather than matched to it -- a grey at
    the bar's own lightness is only 1.05:1 against it and vanishes as a segment; this is
    1.47:1, a visible step, while keeping the thin 1.4px trash strokes at 10.2:1 in white.
  */
  background: #3a424a;
  color: #ffffff;
  border: 0;
  border-radius: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background-color 150ms ease;
}

/*
  :focus-visible is listed alongside :hover on purpose -- the red is the affordance that
  says "this deletes", and a keyboard user tabbing to the button has to get it too.
*/
.remove-vendor-btn:hover:not(:disabled),
.remove-vendor-btn:focus-visible {
  background: #8d312e;
}

.content-layout {
  display: grid;
  gap: 16px;
  grid-template-columns: minmax(0, 70%) minmax(0, 30%);
  align-items: start;
}

.vendor-card {
  background: #ffffff;
  border: 1px solid #d9e2ec;
  border-radius: 10px;
  overflow: hidden;
  /*
    Two rows: the header at its natural height, the body taking the rest. This is the
    track the collapse transition animates -- see .vendor-card-body below.
  */
  display: grid;
  grid-template-rows: auto 1fr;
  /*
    `start`, not the default `stretch`, and this one is about the TRANSITION rather than
    the resting layout. Mid-expand the card's height is already animating toward the full
    list while the `fr` body row still resolves short of it, and `stretch` hands that
    surplus to the `auto` track -- so the blue header itself inflated to a measured 1596px
    at the midpoint and deflated back to 56px, on both this card and its preview twin.
    With `start` the surplus is simply left unoccupied: the header holds 56px across every
    frame while the card still animates 58px -> 6237px. Same defect as the two panes'
    `align-content` (see #vendor-selections), one level down.
  */
  align-content: start;
  transition: grid-template-rows 220ms ease;
}

.vendor-card-header {
  display: flex;
  gap: 8px;
  align-items: center;
  background: #2e5c8d;
  color: #ffffff;
  padding: 10px;
  cursor: pointer;
  user-select: none;
}

/* Debug-only panel (`?debug=true`), kept in step with the pale content treatment. */
.payload-vendor-bar {
  background: #c6d8e0;
  /* Overrides the white text inherited from .vendor-card-header. */
  color: #243b53;
}

/*
  Collapse animates the GRID ROW, not a max-height ceiling (changed 2026-09-18).

  It used to be `max-height: 1200px` expanded / `0` collapsed, and that ceiling was only
  safe because `.package-list` was itself capped at 260px with its own scrollbar -- the
  old comment here said exactly that. Now that a vendor expands fully, the real content
  height is the whole package list: PROQUEST is 393 packages, which is ~130 rows in the
  3-column grid, far past 1200px. Keeping the ceiling would have silently clipped it with
  `overflow: hidden` and no scrollbar to reveal the rest.

  `grid-template-rows: auto 1fr` -> `auto 0fr` animates to the content's real height with
  no magic number to outgrow. `.vendor-card` has exactly two children in both render paths
  -- the header and the body, nothing else -- which is what makes the two-row template
  correct. (The remove button sits inside the *header*, so it does not count against this.)
  Where a browser will not interpolate `fr`, the collapse snaps instead of sliding;
  nothing clips either way.

  Still driven entirely by this class, never by an inline height measured from
  scrollHeight: a measurement taken while any ancestor is display:none reads 0, which is
  how a resumed draft used to render its saved selections invisible.
*/
.vendor-card-body {
  padding: 12px;
  overflow: hidden;
  /* Required for a grid row to shrink below its content. */
  min-height: 0;
  transition: opacity 180ms ease, padding 220ms ease;
  opacity: 1;
}

.vendor-card-collapsed {
  grid-template-rows: auto 0fr;
}

.vendor-card-collapsed .vendor-card-body {
  padding-top: 0;
  padding-bottom: 0;
  pointer-events: none;
  opacity: 0;
}

.vendor-select,
.package-filter {
  width: 100%;
  border: 1px solid #bcccdc;
  border-radius: 8px;
  padding: 8px;
  font-size: 14px;
  background: #ffffff;
}

.vendor-name-display {
  font-size: 14px;
  color: #ffffff;
  font-weight: 500;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.collapse-toggle {
  border-color: transparent;
  color: #ffffff;
  background: transparent;
  width: 36px;
  height: 36px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.collapse-toggle::before {
  content: "";
  width: 10px;
  height: 10px;
  border-right: 2px solid #ffffff;
  border-bottom: 2px solid #ffffff;
  transform: rotate(45deg);
  margin-top: -3px;
  transition: transform 180ms ease, margin-top 180ms ease;
}

.collapse-toggle.is-collapsed::before {
  transform: rotate(-45deg);
  margin-top: 0;
}

.selected-count {
  font-size: 13px;
  padding: 4px 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.18);
  white-space: nowrap;
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 6px;
}

.selected-count-checkbox {
  display: none;
  width: 16px;
  height: 16px;
  cursor: pointer;
}

.vendor-card:not(.vendor-card-collapsed) .selected-count-checkbox {
  display: block;
}

.vendor-card:not(.vendor-card-collapsed) .selected-count {
  cursor: pointer;
  transition: background-color 150ms ease;
}

.vendor-card:not(.vendor-card-collapsed) .selected-count:hover {
  background: rgba(255, 255, 255, 0.28);
}

.selected-count.filter-active {
  background: rgba(255, 255, 255, 0.35);
  font-weight: 600;
}


/*
  Expands to its full height -- the scrollbar for the vendor cards lives on the column
  (#vendor-selections) rather than one per vendor. Do not put max-height/overflow-y back
  here: nesting a scroller per vendor inside a scrolling column is what this replaced.
*/
.package-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px 12px;
  border: 1px solid #d9e2ec;
  border-radius: 8px;
  padding: 8px;
  background: #f9fbfd;
}

.package-item {
  display: flex;
  align-items: start;
  gap: 8px;
  font-size: 14px;
  padding: 4px 2px;
}

.empty-state {
  margin: 0;
  color: #627d98;
  font-size: 14px;
}

/*
  A quiet grey box, deliberately. This panel is a read-back of what you have already
  picked -- it is informational, not something you act on -- so it should not carry the
  heaviest colour on the page. It used to be a saturated filled bar, which competed with
  the actual actions (Add Vendor, Next) for attention purely because of its weight.
  The rule now is: brand blue for things you act on, pale #c6d8e0 for package content.
  Clear All now lives at the top of this panel too (see .selection-preview-clear below) --
  it keeps its own red `.danger` styling rather than tinting the panel, so the rule above
  still holds for the panel itself.
*/
.selection-preview {
  margin-top: 0;
  /*
    No border and no radius: the pane blends into the page, and the pale header band is
    what delineates it. Note this background is the same #f4f6f8 as `body`, so it is
    deliberately invisible -- kept explicit rather than dropped so the pane still reads as
    a surface if the page background ever changes.

    NO PADDING, deliberately. The 10px here was insetting the content from the border this
    pane used to have; with the border gone it did nothing but push Clear All 10px below
    the top of "Package Selection" in the left column (.selection-main), so the two
    columns started at different heights. `.content-layout` aligns the two boxes
    (`align-items: start`) -- this is what aligns their *content*. Re-adding padding
    re-breaks that alignment.
  */
  background: #f4f6f8;
  padding: 0;
  /*
    A flex column so the list below fills whatever is left of the pane. It used to be
    `position: sticky; top: 16px`, which had nothing left to do once step 2 stopped
    scrolling the page: sticky is relative to a scrollport, and above 901px the pane now
    fills a page that does not scroll, while below 901px the panel is static and stacked.
    Left in place it would have read as a live mechanism.
  */
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/*
  Right-aligned so it reads as this column's own action, the same position it held as the
  rightmost button in the old single toolbar row. `align-self`, not a wrapper div with
  `justify-content: flex-end` -- .selection-preview is already the flex column this button
  is a direct child of, so there is nothing a wrapper would do here that align-self does
  not.
*/
.selection-preview-clear {
  align-self: flex-end;
  margin-bottom: 8px;
}

.selection-preview-header {
  display: flex;
  align-items: center;
  gap: 10px;
  /*
    #c6d8e0 cannot carry white text (1.47:1) -- it is a box colour, which is why the
    title and the expand-all control below are dark on it rather than white. Do not
    swap this for a saturated fill without flipping those two back.
  */
  background: #c6d8e0;
  border-radius: 8px;
  padding: 8px 10px;
  margin-bottom: 8px;
}

.selection-preview-title {
  flex: 1;
  display: block;
  font-size: 14px;
  font-weight: 700;
  color: #243b53;
  line-height: 1.2;
}

.preview-expand-all {
  /* Dark on the pale header, not white -- see .selection-preview-header. */
  border-color: rgba(36, 59, 83, 0.35);
  color: #243b53;
  background: transparent;
  padding: 4px 8px;
  font-size: 12px;
  line-height: 1.2;
}

/*
  Sits between Clear All and the "Selected Packages" header, not up with the step's own
  .lead line: it describes what happens to THIS list, so it belongs beside the list, not
  the vendor/package checkboxes on the left. Same accent-border treatment as
  .privacy-notice on step 3, scaled down for the narrower pane -- one family of "here's
  what happens next" notices, not a one-off. A flex child of .selection-preview like
  .selection-preview-search below it; it takes its natural height and the scrollable list
  gives up the rest, same as that search box already does.
*/
.selection-preview-note {
  margin: 0 0 8px;
  padding: 8px 10px;
  background: #f0f4f8;
  border-left: 3px solid #2e5c8d;
  border-radius: 0 6px 6px 0;
  color: #486581;
  font-size: 12.5px;
  line-height: 1.45;
}

.selection-preview-search {
  width: 100%;
  border: 1px solid #bcccdc;
  border-radius: 8px;
  padding: 8px;
  font-size: 13px;
  margin-bottom: 10px;
  background: #ffffff;
}

/*
  The panel's own scroller. It takes whatever height is left under the header and the
  search box rather than the 60vh it used to cap itself at -- same reason as
  #vendor-selections: the two panes are now sized to end at the pinned action bar, so
  neither can run on under it. Reset in the 900px block, where the panel is stacked and
  the page is the scroller again.
*/
.selection-preview-list {
  display: grid;
  gap: 10px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /*
    Same reason as #vendor-selections, plus one extra consequence here: these cards are
    `.vendor-card`s sitting DIRECTLY in the track (no wrapper holding them to the top), so
    a stretched row stretched the card, and the card -- itself a grid, `auto 0fr` when
    collapsed -- passed the space straight on to its header row. That is what made a
    collapsed preview header three times the height of an expanded one.
  */
  align-content: start;
  /*
    ...and `max-content` rows, not the default `auto`, which is the half that makes the
    pane actually scroll. `align-content: start` only decides where a row is PLACED; it
    does not decide how tall the row is. With `auto` rows, an expanded card asks for a
    height through an `fr` track (`.vendor-card` is `auto 1fr`), Chrome cannot resolve
    that against a pane whose own height is definite, and it falls back to sizing the row
    to the PANE -- measured 340.6px for one card, 165px each for two, whatever the pane
    happened to be. The card is then stretched to that row, its `1fr` body row takes the
    remainder, and `.vendor-card-body`'s `overflow: hidden` silently swallows the rest:
    3105px of selected packages rendered into 137px, roughly five rows visible per vendor,
    and the pane reporting scrollHeight == clientHeight so no scrollbar ever appeared.
    `max-content` sizes each row to the card it holds (3283px / 1303px measured), so the
    pane's scrollHeight becomes the real total and the scroller works.

    `align-self: start` on the cards is NOT the fix, though it looks like one: it frees
    each card to its full height while leaving the rows pane-sized, so with two vendors
    the second card is positioned 165px down and the first card's 3283px runs straight
    over it.

    #vendor-selections does not need this only because its cards sit inside
    .vendor-card-wrapper, a flex box, which breaks the same circularity. These cards sit
    directly in the track.
  */
  grid-auto-rows: max-content;
}

.payload-vendor-header {
  justify-content: flex-start;
  padding: 2px 7px;
}

.payload-vendor-name {
  flex: 1;
  font-size: 12px;
  font-weight: 600;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.payload-collapse-toggle {
  width: 22px;
  height: 22px;
  min-width: 22px;
}

.payload-vendor-header .selected-count {
  font-size: 11px;
  padding: 2px 6px;
}

.payload-vendor-body {
  padding: 10px;
}

/* Expands fully; the preview panel's own list is the scroller. */
.payload-package-list {
  margin: 0;
  padding-left: 18px;
  display: grid;
  gap: 5px;
}

.preview-package-item {
  font-size: 13px;
  color: #334e68;
}

.session-footer {
  margin-top: 18px;
  border-top: 1px solid #d9e2ec;
  padding-top: 10px;
}

/* ── Vendor picker modal ──────────────────────────────────────────────────── */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(2px);
}

.modal-backdrop.hidden {
  display: none;
}

.modal-card {
  width: min(480px, calc(100vw - 24px));
  background: #ffffff;
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(15, 23, 42, 0.25);
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 48px);
  overflow: hidden;
}

.modal-title {
  margin: 0;
  padding: 20px 20px 12px;
  font-size: 18px;
  border-bottom: 1px solid #e9edf2;
}

/*
  Vendor picker only, not the Clear All confirm dialog that reuses this same markup: the
  pale #c6d8e0 header mirrors .selection-preview-header's "Selected Packages" band, so the
  two lists that work together read as one family. #c6d8e0 cannot carry white text
  (1.47:1), so the title goes dark on it rather than inheriting the body's lighter default.
*/
#vendor-modal .modal-title {
  background: #c6d8e0;
  color: #243b53;
}

.vendor-search {
  margin: 12px 16px 8px;
  border: 1px solid #bcccdc;
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 14px;
  width: calc(100% - 32px);
}

.modal-vendor-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 8px;
  min-height: 120px;
  max-height: 380px;
}

/* Rendered as a <button> so it is focusable and operable by keyboard. These first
   four declarations undo the browser's default button chrome; everything below
   them is the original row styling. */
.modal-vendor-item {
  width: 100%;
  border: none;
  background: none;
  font-family: inherit;
  color: inherit;
  text-align: left;

  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 10px;
  border-radius: 7px;
  cursor: pointer;
  font-size: 14px;
  gap: 8px;
}

/* Keyboard users need to see where they are. :focus-visible keeps the outline off
   mouse clicks, so this costs the pointer experience nothing. */
.modal-vendor-item:focus-visible {
  outline: 2px solid #2e5c8d;
  outline-offset: -2px;
}

.modal-vendor-item.selected:focus-visible {
  outline-color: #ffffff;
}

.modal-vendor-item:hover {
  background: #eef2f6;
}

.modal-vendor-item.selected {
  background: #2e5c8d;
  color: #ffffff;
}

.modal-vendor-item .vendor-package-count {
  font-size: 12px;
  opacity: 0.65;
  white-space: nowrap;
}

.modal-vendor-item.selected .vendor-package-count {
  opacity: 0.8;
}

.modal-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  padding: 14px 20px;
  border-top: 1px solid #e9edf2;
}

/* ── Step navigation indicator ───────────────────────────────────────────── */

.step-nav {
  margin-bottom: 24px;
}

.step-list {
  display: flex;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
  flex-wrap: wrap;
  gap: 4px;
}

.step-item {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: #9fb3c8;
  white-space: nowrap;
}

.step-item.completed .step-label {
  color: #196b24;
  font-weight: 500;
}

.step-item.active .step-label {
  color: #2e5c8d;
  font-weight: 700;
}

/*
   The label is a <button> on every step (see index.html), so this has to undo the UA's
   button chrome down to the plain text the <span> used to be -- `color: inherit` in
   particular, or a disabled step is greyed by the browser instead of taking the
   #9fb3c8 / green / blue that .step-item and the two rules above hand it.
*/
.step-label {
  padding: 4px 10px;
  border-radius: 999px;
  transition: background 150ms;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: default;
}

.step-item.active .step-label {
  background: #e6ebf1;
}

/* Only completed steps are ever enabled, so the pointer and the hover wash are the whole
   affordance -- there is nothing to suppress on the steps that cannot be clicked.
   #e8f0e9 is the 10%-green wash from the palette, matching the green these labels
   already carry once completed. */
.step-label:enabled {
  cursor: pointer;
}

.step-label:enabled:hover,
.step-label:enabled:focus-visible {
  background: #e8f0e9;
  text-decoration: underline;
}

.step-arrow {
  color: #bcccdc;
  font-size: 14px;
  padding: 0 2px;
}

/* ── Review page ─────────────────────────────────────────────────────────── */

.review-layout {
  display: grid;
  gap: 24px;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
}

.review-card {
  background: #ffffff;
  border: 1px solid #d9e2ec;
  border-radius: 10px;
  overflow: hidden;
}

.review-card-header {
  display: flex;
  align-items: center;
  gap: 8px;
  background: #2e5c8d;
  color: #ffffff;
  padding: 12px 14px;
}

/*
  Same rule as the selections panel: this is the *packages* card on the review step, so it
  gets the pale content treatment while the Contact card keeps the brand blue. That is the
  hierarchy doing the work, not two competing accents. The class is still called `green`
  because `index.html` sets it (`review-card-header green`); it is a modifier name, not a
  colour, and renaming it means editing the markup too.
*/
.review-card-header.green {
  background: #c6d8e0;
  /* #c6d8e0 cannot carry the white text inherited from .review-card-header. */
  color: #243b53;
}

.review-card-title {
  font-size: 14px;
  font-weight: 700;
  margin: 0;
  /* Lets the title wrap before .review-card-download does -- see that rule. */
  min-width: 0;
}

/*
  The download control on the Selected Packages header. `margin-left: auto` rather than a
  spacer element: .review-card-header is already a flex row whose only other child is the
  title, so the auto margin pushes this to the right edge on its own.

  Colours match .preview-expand-all for the same reason that rule gives: the pale #c6d8e0
  band cannot carry white (1.47:1), so the border and the label are dark on it. Do not
  inherit .ghost's white background here -- a white pill on the pale band reads as an input,
  not a button.
*/
.review-card-download {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  border-color: rgba(36, 59, 83, 0.35);
  color: #243b53;
  background: transparent;
  padding: 4px 8px;
  font-size: 12px;
  line-height: 1.2;
  /*
    At 320px the header band is ~255px of usable width and the label wrapped to two lines,
    growing the band with it. The heading is the one that should give: `nowrap` here plus
    `min-width: 0` on the title lets "Selected Packages" wrap instead, which reads as a
    heading on two lines rather than as a broken control.
  */
  white-space: nowrap;
}

.review-card-download:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.55);
}

.review-card-body {
  padding: 16px;
}

.contact-field {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: 4px 12px;
  font-size: 14px;
  padding: 6px 0;
  border-bottom: 1px solid #f0f4f8;
}

.contact-field:last-child {
  border-bottom: none;
}

.contact-field-label {
  color: #627d98;
  font-size: 13px;
}

.contact-field-value {
  color: #1f2933;
  font-weight: 500;
  word-break: break-word;
}

/*
  Sits directly under the five personal fields on step 3, never on step 1. A notice
  describing collection that is not happening trains people to ignore notices, so it
  belongs beside the inputs it actually describes.
*/
.privacy-notice {
  margin: 4px 0 0;
  padding: 10px 12px;
  background: #f0f4f8;
  border-left: 3px solid #2e5c8d;
  border-radius: 0 6px 6px 0;
  color: #486581;
  font-size: 13px;
  line-height: 1.5;
}

.review-actions {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-top: 28px;
  justify-content: flex-end;
}

.review-status {
  margin-top: 16px;
  padding: 12px 16px;
  border-radius: 8px;
  font-size: 14px;
}

.review-status.success {
  background: #e8f0e9;
  border: 1px solid #52905b;
  color: #196b24;
}

.review-status.error {
  background: #fff4f4;
  border: 1px solid #f3b0b0;
  color: #7f1d1d;
}

.json-preview {
  margin-top: 16px;
  padding: 14px;
  border: 1px solid #d9e2ec;
  border-radius: 10px;
  background: #ffffff;
}

.json-preview h3 {
  margin: 0 0 8px;
  font-size: 14px;
  color: #334e68;
}

.json-preview-block {
  margin: 0 0 14px;
  padding: 10px;
  border-radius: 8px;
  border: 1px solid #e4e7eb;
  background: #f8fafc;
  font-size: 12px;
  line-height: 1.45;
  color: #243b53;
  white-space: pre-wrap;
  word-break: break-word;
}

.json-preview-block:last-child {
  margin-bottom: 0;
}

.review-selection-list {
  display: grid;
  gap: 10px;
  max-height: 60vh;
  overflow-y: auto;
}

@media (max-width: 900px) {
  .review-layout {
    grid-template-columns: 1fr;
  }
}

/* ── Success / confirmation screen ───────────────────────────────────────── */

.confirmation-card {
  width: min(560px, 100%);
  background: #ffffff;
  border: 1px solid #d9e2ec;
  border-radius: 12px;
  padding: 36px 28px;
  text-align: center;
}

.confirmation-icon {
  font-size: 48px;
  margin-bottom: 12px;
}

.confirmation-card h2 {
  margin: 0 0 10px;
  color: #196b24;
}

.confirmation-card p {
  color: #52606d;
  margin: 0 0 20px;
  font-size: 14px;
}

.confirmation-id {
  display: inline-block;
  font-family: monospace;
  font-size: 13px;
  background: #f0f4f8;
  border: 1px solid #d9e2ec;
  border-radius: 6px;
  padding: 6px 12px;
  color: #334e68;
  word-break: break-all;
}

@media (max-width: 900px) {
  .content-layout {
    grid-template-columns: 1fr;
  }

  /*
    Stacked layout: hand the scrolling back to the page. Two bounded panes one above the
    other on a phone just makes two small windows to scroll inside a third. `flex: none`
    puts the preview list back on its content height -- the app-shell block above does
    not apply here, but the base `flex: 1` does.
  */
  #vendor-selections,
  .selection-preview-list {
    overflow-y: visible;
  }

  .selection-preview-list {
    flex: none;
  }

  /* No scrollbar to clear once the column is not a scroller. */
  #vendor-selections {
    padding-right: 0;
    scrollbar-gutter: auto;
  }

  .package-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 620px) {
  .vendor-card-header {
    flex-wrap: wrap;
  }

  .vendor-select {
    max-width: none;
  }


  .package-list {
    grid-template-columns: 1fr;
  }
}

/* ── Confirmation page ─────────────────────────────────────────────────────── */

/*
  No .button rule existed anywhere in this file -- every existing button uses .primary,
  .danger or .ghost on a <button> element. The confirmation page's downloads are <a> tags
  (so "open in new tab" and right-click-save keep working), so they need their own class;
  these two mirror .primary and .ghost's colors so the page reads as the same site.
*/
.button {
  display: inline-block;
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 8px 14px;
  font-size: 14px;
  text-decoration: none;
  background: #2e5c8d;
  color: #ffffff;
}

.button--secondary {
  border-color: #bcccdc;
  background: #ffffff;
  color: #334e68;
}

.confirmation {
  max-width: 46rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
}

.confirmation__status {
  padding: 1rem 0;
}

.confirmation__title {
  margin: 0 0 0.5rem;
}

.confirmation__lede {
  margin: 0 0 1.5rem;
}

.confirmation__meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 1rem;
  margin: 0 0 1.5rem;
}

.confirmation__meta dt {
  font-weight: 600;
}

.confirmation__meta dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.confirmation__downloads {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 0 0 2rem;
}

.confirmation__subtitle {
  margin: 0 0 0.75rem;
  font-size: 1.1rem;
}

.confirmation__vendor {
  margin: 1.25rem 0 0.35rem;
  font-size: 1rem;
}

.confirmation__packages {
  margin: 0;
  padding-left: 1.25rem;
}

/* ── Site banner ─────────────────────────────────────────────────────────── */

/*
  The same markup block is repeated verbatim at the top of index.html and
  confirmation.html. There is no templating here -- nginx serves these as static files
  -- so "every page" means those two: review.html and vendor.html are meta-refresh
  redirects that are never painted, and adding chrome to them would only make the
  redirect flash visible.

  The banner is one image rather than art behind HTML text: the cloud mark, the wordmark,
  the divider rule and the "Collection Analysis" line are all vector paths inside
  CloudSource-Collection-Analysis-banner.svg. So there is nothing here to type-set -- only
  a bar to paint and a size to cap.
*/
.site-banner {
  /*
    The lockup SVG has no ground of its own (transparent, and its "Source" half is white),
    so this is what makes it legible. Load-bearing, not decoration: drop the colour and
    "Source" and the heading disappear rather than the bar turning white.
  */
  background-color: #071530;

  /*
    81px is a DECLARED height, and that is the whole point of this rule.

    It used to be emergent -- the bar was 18px of padding plus whatever height the image
    happened to resolve to, which landed on ~81px by arithmetic and nowhere said so. Every
    later edit to the width cap or the padding moved it silently, so the intended 81px kept
    getting lost and having to be re-fixed. Now the bar is 81px until someone changes this
    line deliberately, and the image is fitted to it rather than the reverse.

    DO NOT convert this back into padding + an image height.
  */
  height: 81px;
  padding: 0 clamp(12px, 1.5vw, 24px);

  /* What lets the image shrink inside a fixed-height bar without clipping or stretching. */
  display: flex;
  align-items: center;
}

.site-banner__image {
  display: block;
  /*
    Capped on BOTH axes with the aspect ratio left to the SVG, so the lockup always fits
    whichever constraint bites first: max-height holds it inside the 81px bar on a desktop
    (45px -> ~436px wide, the lockup being cropped tight at ~9.7:1), and max-width takes
    over on a narrow viewport, where the lockup scales down and the bar stays 81px.
    No object-fit crop -- the SVG is cropped tight to the lettering already, so there is
    nothing to keep clear of one.
  */
  max-height: 45px;
  max-width: 100%;
  width: auto;
}

/*
  Positioning context for the logo link, and it has to be the image's box EXACTLY -- the
  link below is sized as a percentage of it. `fit-content` is what does that: it shrink-
  wraps to the image on a desktop (436px) and is itself capped at the space available, so
  the image's own `max-width: 100%` still bites on a narrow viewport and the two boxes stay
  the same width (measured identical to the un-wrapped banner at 1280/600/390/320px). A
  plain block would be the full width of the bar and the percentage would mean nothing.

  `line-height: 0` keeps the inline image from adding a descender strip under itself, which
  would fight the bar's declared 81px.
*/
.site-banner__lockup {
  position: relative;
  display: block;
  width: fit-content;
  line-height: 0;
}

/*
  The CloudSource logo, as a link to cloudsource.net. An overlay, not a wrapper around the
  <img>: the cloud mark, the wordmark, the divider rule and the "Collection Analysis"
  heading are all paths in the same SVG, so an <a> around the image would hand the product
  heading to the brand link as well.

  Sized in PERCENT, never pixels -- the image scales with the viewport, and a percentage box
  tracks the artwork where a pixel one would drift off it. The logo lockup measures
  x 0..598.3 in the SVG's 1957-wide viewBox, so 30.57%; the divider rule does not start
  until x 678, so nothing but the logo is under here. Full height, because the artwork left
  of that rule is the logo and nothing else, and vertical slop makes the target easier to
  hit. Re-crop the SVG and this number has to be re-measured with it.
*/
.site-banner__logo-link {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 30.57%;
}

/*
  White rather than the brand blue the rest of the app focuses with -- this ring sits on the
  navy bar, where #2e5c8d all but disappears. Same reasoning as .modal-vendor-item.selected.
*/
.site-banner__logo-link:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
  border-radius: 2px;
}

/*
  Too narrow for a guid to sit between two buttons without pushing one of them off. Drop
  it from the pinned bar and give the page-foot copy back -- the bar keeps doing its real
  job (Back and Next always reachable) and the id is still on the page, one scroll away.
*/
@media (max-width: 700px) {
  .pinned-submission-id {
    display: none;
  }

  body.pinned-actions-active .session-footer {
    display: block;
  }
}
