/* CarNerd Garage - large touch targets, short lists, quiet chrome.
   Designed to be read at arm's length under a hood.

   Light by default, with a dark switch in the menu. Every component below
   reads its colour from a semantic token, so the two themes are the two token
   blocks and nothing else. Brand colours (red, navy) are fixed in both.

   Every colour used for text was checked against its own background for WCAG
   AA (4.5:1). The raw brand red only clears AA-large, so it carries buttons,
   rules and the brand - never small type. */

:root {
  /* Brand, straight from the logo. Identical in both themes. */
  --cn-red: #e10e11;
  --cn-red-hover: #b3121a;
  --cn-navy: #092c57;
  --cn-navy-deep: #071a3d;
  --cn-chrome: #d6dbe4;   /* on the navy navbar, which never changes */
}

/* -- Light, the default --------------------------------------------------- */
:root,
[data-bs-theme="light"] {
  --garage-page: #eef1f7;
  --garage-card: #ffffff;
  --garage-row-alt: #f6f8fc;
  --garage-hover: #eef3fb;

  --garage-input: #ffffff;
  --garage-input-focus: #ffffff;
  --garage-input-border: #c3cbd9;
  --garage-disabled-bg: #eceff5;
  --garage-disabled-ink: #7c879b;

  --garage-line: #d8dfeb;
  --garage-line-strong: #c3cbd9;

  --garage-ink-strong: #0f1830;   /* titles and values     17.6:1 */
  --garage-ink: #16203a;          /* body                  16.1:1 */
  --garage-ink-muted: #55627d;    /* labels and subtitles   6.1:1 */
  /* The job name in an Upcoming row, beside the near-black vehicle name.
     7.7:1 on a card, and 2.3:1 apart from #0f1830 - different enough to read
     as two things, dark enough to still be a heading. Measured. */
  --garage-ink-charcoal: #47536b; /* Upcoming job name      7.7:1 */
  /* 3.6:1 on a card, 3.2:1 on the page - below AA for body text, which is
     why it is only ever placeholder and disabled-control text. Measured;
     this line used to claim 4.5:1, which was the figure for a different
     background. Nothing here should use it for something readable. */
  --garage-ink-faint: #7c879b;    /* placeholders           3.6:1 */

  --garage-link: #1c5fb0;
  --garage-accent: #1c5fb0;
  /* What goes ON the accent when it is used as a fill. The accent is dark
     here and pale in the dark theme, so the text on it has to flip with it -
     white on the pale one is 2.5:1, which is unreadable. */
  --garage-on-accent: #ffffff;    /* on the light accent    6.3:1 */
  --garage-copper: #a1521c;
  --garage-warning: #8a5a00;
  --garage-ok: #1d7a3c;
  --garage-cached-edge: #7bb98f;
  /* Two layers: a tight one that draws the edge, a wide soft one that lifts the
     panel off the page. The lift variant is the same shape, further off, for
     hover - so a tile rises rather than changing colour. */
  --garage-shadow: 0 1px 2px rgb(16 26 51 / 10%), 0 6px 16px rgb(16 26 51 / 10%);
  --garage-shadow-lift: 0 2px 4px rgb(16 26 51 / 12%), 0 10px 24px rgb(16 26 51 / 16%);

  --bs-body-color: var(--garage-ink);
  --bs-secondary-color: var(--garage-ink-muted);
  --bs-border-color: var(--garage-line);
  --bs-link-color-rgb: 28, 95, 176;
  --bs-link-hover-color-rgb: 19, 71, 135;
  --bs-danger-rgb: 192, 34, 34;
}

/* -- Dark ----------------------------------------------------------------- */
[data-bs-theme="dark"] {
  --garage-page: #131926;
  --garage-card: #1e2637;
  --garage-row-alt: #232c40;
  --garage-hover: #2b3750;

  --garage-input: #2b3750;
  --garage-input-focus: #33405c;
  --garage-input-border: #46557a;
  --garage-disabled-bg: #232c40;
  --garage-disabled-ink: #8e99ad;

  --garage-line: #3a4863;
  --garage-line-strong: #4a5b7d;

  --garage-ink-strong: #ffffff;
  --garage-ink: #e8ecf2;
  --garage-ink-muted: #b8c2d4;
  --garage-ink-charcoal: #b8c2d4;  /* 8.4:1 on a dark card */
  --garage-ink-faint: #9aa6bb;

  --garage-link: #6aa8ea;
  --garage-accent: #6aa8ea;
  --garage-on-accent: #16203a;    /* on the pale accent     6.4:1 */
  --garage-copper: #e8823f;
  --garage-warning: #ffca6a;
  --garage-ok: #6ec07a;
  --garage-cached-edge: #2f5d3a;
  /* On a dark page a shadow alone is almost invisible, so the dark theme leans
     on the border for the edge and uses the shadow only for depth. */
  --garage-shadow: 0 1px 3px rgb(0 0 0 / 55%), 0 6px 18px rgb(0 0 0 / 38%);
  --garage-shadow-lift: 0 2px 6px rgb(0 0 0 / 60%), 0 12px 28px rgb(0 0 0 / 48%);

  --bs-body-color: var(--garage-ink);
  --bs-secondary-color: var(--garage-ink-muted);
  --bs-border-color: var(--garage-line);
  --bs-link-color-rgb: 106, 168, 234;
  --bs-link-hover-color-rgb: 154, 199, 245;
  --bs-danger-rgb: 255, 107, 107;
}

/* One dial for the whole interface. Everything below is sized in rem, so type,
   padding and control heights all scale from here. A percentage rather than a
   px value, so a reader who has raised their browser's default still wins. */
html {
  font-size: 106.25%;  /* 17px against the usual 16px default */
}

body {
  background: var(--garage-page);
  color: var(--garage-ink);
  -webkit-text-size-adjust: 100%;
}

/* -- The badge behind everything ------------------------------------------ */

/* Nearly the width of the screen, sitting under the content. Its own fixed
   element rather than a background on <body> for two reasons: opacity here
   cannot fade the text on top of it, and `fixed` keeps it still while the
   page scrolls instead of tiling away down a long history.
   `min()` caps it on a wide monitor, where 90vw would be a 1,700px badge. */
.garage-watermark {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(92vw, 900px);
  aspect-ratio: 1;
  background: url("img/logo.png") center / contain no-repeat;
  /* Low enough that body text keeps its contrast ratio - the badge is a hint
     that this is CarNerd, not something to read. */
  opacity: 0.05;
  z-index: 0;
  pointer-events: none;
}

/* Slightly stronger on a dark page: the same 5% over near-black reads as
   almost nothing. */
[data-bs-theme="dark"] .garage-watermark {
  opacity: 0.08;
}

/* -- The badge intro, on the sign-in page ---------------------------------
   The same element does the whole thing: the badge fades up large, holds a
   beat, then settles to the faint watermark it sits at on every other
   screen, while the form arrives over the top of it. One element rather than
   a splash overlay, because the logo is not replaced by the watermark - it
   *becomes* it, and anything that cross-fades two copies shows the seam.

   Three ways out of it, because an animation between somebody and a password
   box has to be escapable:
     - once per browser session (the head script sets `intro-done`)
     - any tap or keypress finishes it immediately
     - never, for anyone who has asked for less motion

   The form is only hidden while the animation is actually running. Every
   rule below is additive, so if the stylesheet half-loads or a keyframe is
   unsupported, the page is simply the page. */
.garage-intro .garage-watermark {
  animation: intro-badge 2.2s ease both;
}

[data-bs-theme="dark"] .garage-intro .garage-watermark {
  animation-name: intro-badge-dark;
}

.garage-intro .garage-main {
  /* Starts at 1.4s - overlapping the badge's settle rather than waiting for
     it, so the form feels like it arrives with the badge instead of after a
     pause. Usable by 2s. */
  animation: intro-content 0.6s ease 1.4s both;
}

@keyframes intro-badge {
  0%   { opacity: 0;    transform: translate(-50%, -50%) scale(1.05); }
  25%  { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  70%  { opacity: 0.85; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0.05; transform: translate(-50%, -50%) scale(1); }
}

/* Same curve, landing on the dark theme's resting opacity rather than the
   light one's - otherwise the badge ends a shade too faint to see. */
@keyframes intro-badge-dark {
  0%   { opacity: 0;    transform: translate(-50%, -50%) scale(1.05); }
  25%  { opacity: 0.9;  transform: translate(-50%, -50%) scale(1); }
  70%  { opacity: 0.9;  transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0.08; transform: translate(-50%, -50%) scale(1); }
}

@keyframes intro-content {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Seen it this session, or chose to get on with it. Everything at rest. */
.intro-done .garage-intro .garage-watermark,
.intro-done .garage-intro .garage-main,
.garage-intro.intro-skip .garage-watermark,
.garage-intro.intro-skip .garage-main {
  animation: none;
}

/* The cards are opaque, so they would hide the badge wherever they fall.
   Lifting the content above it and leaving the gaps between cards is what
   makes it visible at all.

   The navbar is deliberately NOT in here. It carries Bootstrap's
   `.sticky-top`, which is `position: sticky; z-index: 1020` - and this rule,
   loading after Bootstrap and matching its specificity, was quietly
   replacing both. That cost two things: the navbar stopped sticking, and the
   menu it contains dropped to the same z-index as the page, which paints
   later and so covered it. The badge does not need the nav lifted anyway:
   1020 is already well clear of the watermark's 0. */
.garage-main {
  position: relative;
  z-index: 1;
}

/* Someone who has asked for less motion or less contrast does not want a
   picture competing with the text. */
@media (prefers-contrast: more) {
  .garage-watermark {
    display: none;
  }
}

/* No intro at all for anyone who has asked for less motion. The sign-in form
   is there the instant the page is, which is what they asked for. */
@media (prefers-reduced-motion: reduce) {
  .garage-intro .garage-watermark,
  .garage-intro .garage-main {
    animation: none;
  }
}

/* -- Forms ---------------------------------------------------------------- */

.form-control,
.form-select {
  background-color: var(--garage-input);
  border-color: var(--garage-input-border);
  color: var(--garage-ink);
}

/* Not yet available - the Make and Model pickers before a year is chosen.
   Muted within the palette rather than Bootstrap's unrelated grey. */
.form-control:disabled,
.form-select:disabled {
  background-color: var(--garage-disabled-bg);
  border-color: var(--garage-line);
  color: var(--garage-disabled-ink);
}

.form-control:focus,
.form-select:focus {
  background-color: var(--garage-input-focus);
  border-color: var(--garage-accent);
  color: var(--garage-ink-strong);
  box-shadow: 0 0 0 0.25rem rgb(28 95 176 / 20%);
}

[data-bs-theme="dark"] .form-control:focus,
[data-bs-theme="dark"] .form-select:focus {
  box-shadow: 0 0 0 0.25rem rgb(106 168 234 / 25%);
}

.form-control::placeholder {
  color: var(--garage-ink-faint);
  opacity: 1;
}

/* The file-picker button inside the control, which Bootstrap tints separately. */
.form-control::file-selector-button {
  background-color: var(--garage-row-alt);
  color: var(--garage-ink);
  border-color: var(--garage-input-border);
}

/* Every field label: small caps, and the body ink rather than the muted grey.
   At 6.1:1 the old grey cleared AA and still read as faint under a work light
   with the phone at arm's length; this is 16.1:1 on a card. Letter-spacing
   because caps set tight are harder to read, not easier, and the slight extra
   weight keeps them from looking shouted. */
.form-label {
  color: var(--garage-ink);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  margin-bottom: 0.3rem;
}

/* Plenty of labels are written `class="form-label small text-secondary"`.
   Bootstrap's colour utilities carry !important, so without matching it the
   muted grey wins and the label stays faint - which is the whole thing being
   fixed here. The alternative was editing forty templates to drop a class. */
.form-label.text-secondary,
.form-label.small {
  color: var(--garage-ink) !important;
  font-size: 0.78rem;
}

/* Bootstrap's check labels sit beside their box and read as a sentence, not
   as a field name - "Email me when something is due" in caps would be
   shouting an instruction. */
.form-check-label {
  text-transform: none;
  letter-spacing: 0;
}

/* -- Buttons -------------------------------------------------------------- */

/* Primary actions wear the brand red. White on #e10e11 is 4.9:1, so the
   oversized labels stay legible in daylight through a windscreen. */
.btn-primary {
  --bs-btn-bg: var(--cn-red);
  --bs-btn-border-color: var(--cn-red);
  --bs-btn-hover-bg: var(--cn-red-hover);
  --bs-btn-hover-border-color: var(--cn-red-hover);
  --bs-btn-active-bg: var(--cn-red-hover);
  --bs-btn-active-border-color: var(--cn-red-hover);
  --bs-btn-disabled-bg: var(--cn-red);
  --bs-btn-disabled-border-color: var(--cn-red);
}

/* Bootstrap's own greys sit at 4.1:1 or vanish entirely on white, so both
   outline styles are re-pointed at the theme's ink and rules. */
.btn-outline-secondary,
.btn-outline-light {
  --bs-btn-color: var(--garage-ink);
  --bs-btn-border-color: var(--garage-line-strong);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--cn-navy);
  --bs-btn-hover-border-color: var(--cn-navy);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--cn-navy);
  --bs-btn-active-border-color: var(--cn-navy);
}

/* The "spend an OLP request" actions. Bootstrap's cyan manages 1.7:1 on white,
   so they follow the theme's link colour instead of glowing. */
.btn-outline-info {
  --bs-btn-color: var(--garage-link);
  --bs-btn-border-color: var(--garage-link);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--garage-link);
  --bs-btn-hover-border-color: var(--garage-link);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--garage-link);
  --bs-btn-active-border-color: var(--garage-link);
  --bs-btn-disabled-color: var(--garage-ink-faint);
  --bs-btn-disabled-border-color: var(--garage-line);
}

.btn:focus-visible {
  box-shadow: 0 0 0 0.25rem rgb(28 95 176 / 35%);
}

[data-bs-theme="dark"] .btn:focus-visible {
  box-shadow: 0 0 0 0.25rem rgb(106 168 234 / 40%);
}

/* Primary actions are deliberately oversized for gloved hands. */
.btn-garage {
  min-height: 58px;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Not a primary action, but not a footnote either - it is the way back to the
   type, the photo and the lookup name. Sized to the same 48px tap target as
   the rest of the page's controls. */
.btn-edit-vehicle {
  min-height: 48px;
  padding-inline: 1.1rem;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

.btn-photo {
  min-height: 52px;
  font-weight: 600;
  letter-spacing: 0.03em;
}

/* The two primary actions: stacked, centred and narrower than the page so
   they read as a pair rather than as two banners. */
.home-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
}

.home-actions .btn {
  width: 100%;
  max-width: 280px;
}

/* -- Navbar. Always flag navy, in both themes: it is the badge's outer ring
      and the one fixed point on the page. -------------------------------- */

.garage-nav {
  background: var(--cn-navy);
  border-bottom: 2px solid var(--cn-red);
}

.navbar-brand,
.navbar-brand:hover,
.navbar-brand:focus {
  color: #fff;
  letter-spacing: 0.06em;
}

/* The badge is detailed artwork, so it needs a good deal more room than a
   typical navbar icon before the car and the lettering turn to mush - which
   is exactly what happened at the icon sizes this started out at. */
.brand-logo {
  width: 3.1rem;
  height: 3.1rem;
  flex: 0 0 auto;
  object-fit: contain;
}

/* Badge sign-off at the foot of every page. Quiet enough to ignore, present
   enough that the brand is not confined to the top bar. */
.garage-footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 1.6rem 1rem 2.2rem;
  margin-top: 1rem;
  border-top: 1px solid var(--garage-line);
  color: var(--garage-ink-muted);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.footer-mark {
  width: 2.4rem;
  height: 2.4rem;
  object-fit: contain;
  opacity: 0.85;
}

.footer-version {
  font-weight: 400;
  opacity: 0.75;
}

/* Sign-in and first-run: the badge is the page, so it gets room. At this size
   the artwork's own lettering is legible, which is why the heading beside it
   is carried for screen readers only rather than repeated on screen. */
.brand-hero {
  width: 208px;
  height: 208px;
  object-fit: contain;
  display: block;
  margin: 0 auto 1.4rem;
}

/* Fallback garage glyph, still used as the photo placeholder. */
.brand-icon {
  width: 1.5rem;
  height: 1.5rem;
  color: var(--garage-accent);
  flex: 0 0 auto;
}

/* Icon-only nav controls: no border, but a full 44px tap target. Fixed
   colours because the bar behind them is navy in both themes. */
.btn-nav {
  border: 0;
  padding: 0;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--cn-chrome);
}

.btn-nav svg {
  width: 22px;
  height: 22px;
}

.btn-nav:hover,
.btn-nav:focus,
.btn-nav[aria-expanded="true"] {
  color: #fff;
}

.dropdown-menu {
  background: var(--garage-card);
  border: 1px solid var(--garage-line);
  box-shadow: var(--garage-shadow);
}

.dropdown-item {
  color: var(--garage-ink);
  padding: 0.6rem 1rem;
}

.dropdown-item:hover,
.dropdown-item:focus {
  background: var(--cn-navy);
  color: #fff;
}

/* Dark-mode switch: its state sits right-aligned in the menu row. */
.theme-state {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--garage-ink-muted);
}

.dropdown-item:hover .theme-state,
.dropdown-item:focus .theme-state {
  color: var(--cn-chrome);
}

/* -- Structure and titles ------------------------------------------------- */

.garage-main {
  max-width: 45rem;
}

/* Section headings lead with a short red rule so the eye finds the breaks in
   a long screen without reading a word. */
.section-label {
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--garage-ink-strong);
  margin: 1.9rem 0 0.7rem;
  padding-left: 0.6rem;
  border-left: 3px solid var(--cn-red);
  line-height: 1.25;
}

/* The heading inside a panel. Underscored, so the panel reads as a block with
   a header rather than as an undifferentiated stack of fields. */
.panel-title {
  text-transform: uppercase;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  color: var(--garage-ink-muted);
  padding-bottom: 0.5rem;
  margin-bottom: 0.8rem;
  border-bottom: 1px solid var(--garage-line);
}

/* Panels read as distinct surfaces: lifted clear of the page, with a defined
   edge and enough shadow to sit above it. Tabular figures throughout, so
   mileages, dates and capacities line up down every list. */
.garage-card {
  background: var(--garage-card);
  border: 1px solid var(--garage-line);
  border-radius: 0.85rem;
  box-shadow: var(--garage-shadow);
  font-variant-numeric: tabular-nums;
}

/* -- Tiles and lists ------------------------------------------------------ */

/* Two on a phone, three from tablet width up - fixed counts rather than
   auto-fit, because auto-fit put four across a wide screen and left the last
   row short. Six tiles divide evenly either way. */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.6rem;
}

@media (min-width: 600px) {
  .tile-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.btn-tile {
  min-height: 66px;
  text-align: left;
  padding: 0.75rem 1rem;
  background: var(--garage-card);
  border: 1px solid var(--garage-line);
  border-radius: 0.85rem;
  box-shadow: var(--garage-shadow);
  color: var(--garage-ink);
  font-variant-numeric: tabular-nums;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}

/* In a grid every tile is the row's height, so the label and its subtitle sit
   at the top instead of floating in the middle of a tall box. */
.tile-grid .btn-tile {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  min-height: 5.5rem;
  line-height: 1.25;
}

.btn-tile:hover,
.btn-tile:focus {
  border-color: var(--garage-accent);
  background: var(--garage-hover);
  color: var(--garage-ink-strong);
  box-shadow: var(--garage-shadow-lift);
  transform: translateY(-1px);
}

/* Nobody wants the page to twitch. */
@media (prefers-reduced-motion: reduce) {
  .btn-tile {
    transition: none;
  }

  .btn-tile:hover,
  .btn-tile:focus {
    transform: none;
  }
}

.btn-tile .tile-sub {
  display: block;
  font-size: 0.8rem;
  color: var(--garage-ink-muted);
  font-weight: 400;
}

.upcoming-item {
  border-bottom: 1px solid var(--garage-line);
  padding: 0.7rem 0;
}

.upcoming-item:last-child {
  border-bottom: 0;
}

.upcoming-vehicle {
  font-size: 0.82rem;
  color: var(--garage-ink-muted);
}

.upcoming-title {
  font-weight: 600;
  color: var(--garage-ink-strong);
}

/* "Tubby - Oil & Filter" on one line. Both halves bold, a shade apart, so
   either can be picked out without the row reading as a sentence. Wraps as a
   unit on a narrow screen rather than breaking mid-name. */
.upcoming-headline {
  font-weight: 600;
  line-height: 1.25;
}

.upcoming-headline .uh-vehicle {
  color: var(--garage-ink-strong);
}

.upcoming-headline .uh-service {
  color: var(--garage-ink-charcoal);
}

/* Quieter than either name: it is punctuation, not content. */
.upcoming-headline .uh-sep {
  color: var(--garage-ink-faint);
  font-weight: 400;
  padding: 0 0.1rem;
}

.upcoming-due {
  font-size: 0.88rem;
  color: var(--garage-ink-muted);
}

.history-row {
  border-bottom: 1px solid var(--garage-line);
  padding: 0.75rem 0;
}

.history-row:last-child {
  border-bottom: 0;
}

.vehicle-context {
  border-bottom: 1px solid var(--garage-line);
  padding: 0.8rem 0 0.6rem;
}

.vehicle-context-name {
  letter-spacing: 0.02em;
  color: var(--garage-ink-strong);
  font-weight: 700;
  font-size: 1.25rem;
  line-height: 1.2;
  display: block;
}

/* Nickname leads the name, tinted copper so the two read as separate facts. */
.vh-nick {
  color: var(--garage-copper);
}

.min-w-0 {
  min-width: 0;
}

/* -- The vehicle page -------------------------------------------------- */

/* Mileage is read far more often than it is changed, so it reads as a value
   with a pencil beside it rather than a text box and a button sitting there
   permanently. Big enough to take in at a glance from the driveway. */
.vh-mileage {
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--garage-ink-strong);
  font-variant-numeric: tabular-nums;
}

/* Five panels that used to run down the page now cost one screen.

   Buttons rather than filing-cabinet tabs. A tab claims the panel below is
   attached to it, and every panel but the first carries its own title, so the
   join never read as one - a button that is simply lit says the same thing
   without the claim. Separated, with a gap, so each reads as its own control
   rather than as an edge of the next one. */
.vh-tabs {
  display: flex;
  gap: 0.4rem;
  border-bottom: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  /* Scrolls sideways on a narrow phone rather than wrapping to two rows,
     which would push the primary action further down than the tabs save. */
  scrollbar-width: none;
  padding-bottom: 0.15rem;
}

.vh-tabs::-webkit-scrollbar {
  display: none;
}

/* The home screen's strip wraps instead of scrolling. Four buttons do not fit
   across a phone, and there the two that fall off are whole panels nobody
   would know to look for - where on the job screen the primary action sits
   below the strip and a second row would push it down the page. */
.vh-tabs-wrap {
  flex-wrap: wrap;
  overflow-x: visible;
  row-gap: 0.4rem;
  margin-top: 0.9rem;
}

.vh-tabs .nav-link {
  white-space: nowrap;
  border: 1px solid var(--garage-line-strong);
  border-radius: 0.5rem;
  background: var(--garage-card);
  color: var(--garage-ink);
  padding: 0.5rem 0.9rem;
  /* Capitals, like every other label on the screen. A little smaller and a
     little spaced, because set in caps at the old size the row ran off the
     side of a phone. */
  font-size: 0.86rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 600;
}

.vh-tabs .nav-link:hover {
  background: var(--garage-hover);
  color: var(--garage-ink-strong);
}

/* The panel on screen. Blue and filled, like Save - the one thing on this row
   that is currently true. */
.vh-tabs .nav-link.active,
.vh-tabs .nav-link.active:hover {
  background: var(--garage-accent);
  border-color: var(--garage-accent);
  color: var(--garage-on-accent);
  font-weight: 700;
}

/* The count has to stay readable on both, so it flips with the button. */
.vh-tabs .nav-link.active .vh-count {
  background: var(--garage-on-accent);
  color: var(--garage-accent);
}

.vh-tab-body {
  padding-top: 0.75rem;
}

/* A panel's own heading sits directly under the buttons, so it does not need
   the full gap the same label gets when it separates two stacked sections. */
.vh-tab-body .tab-pane > .section-label:first-child {
  margin-top: 0.5rem;
}

/* A field name and its buttons on one row: the name left, the tools right,
   and the box they belong to directly underneath. */
.field-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.3rem;
}

.field-head .text-tools {
  margin-bottom: 0;
}

/* The count of outstanding recalls, on the tab itself. */
.vh-badge {
  background: var(--cn-red, #e10e11);
  color: #fff;
  font-size: 0.7rem;
  vertical-align: middle;
}

/* A settled recall folds to one line: tick, number, name, and how it ended.
   The details are worth reading while it is a decision to make and in the way
   once it is not, so they go behind a disclosure rather than off the screen
   entirely - the record is still there for anyone who wants it. */
.recall-summary {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.1rem 0.5rem;
  cursor: pointer;
  list-style: none;
}

.recall-summary::-webkit-details-marker {
  display: none;
}

.recall-mark {
  color: var(--garage-ink-faint);
  font-weight: 700;
}

.recall-mark.is-done {
  color: var(--garage-ok);
}

/* Tabular so a column of campaign numbers lines up down the list. */
.recall-ref {
  color: var(--garage-ink-muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Takes what is left and gives it back when the row has to wrap, rather than
   pushing the state off the end. */
.recall-name {
  flex: 1 1 8rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--garage-ink);
}

.recall-state {
  color: var(--garage-ink-muted);
  font-size: 0.82rem;
  font-weight: 600;
  white-space: nowrap;
}

/* 5.4:1 on a card in light, 6.8:1 in dark. Measured. */
.recall-state.is-done {
  color: var(--garage-ok);
}

/* Open one and the detail reads as detail, not as another row. */
.recall-item[open] > .recall-summary {
  margin-bottom: 0.1rem;
}

/* The answers to a recall, on their own row across the card. In a column
   beside the text they had a few characters to work with, so "Not mine to do"
   broke onto two lines and the remove button fell onto a third. The remove
   button is pushed to the far end, away from the four it is not like. */
.recall-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.6rem;
}

/* -- The Admin sharing grid ---------------------------------------------- */

/* A real matrix: people across the top, vehicles down the side. It scrolls
   sideways rather than reflowing, because reflowing loses the one thing a grid
   is for - reading a column down and a row across. */
.share-grid {
  border: 1px solid var(--bs-border-color);
  border-radius: 0.5rem;
  box-shadow: var(--garage-shadow);
  background: var(--bs-body-bg);
}

.share-grid table {
  margin-bottom: 0;
}

/* The vehicle name stays put while the people scroll past it. */
.share-name {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--bs-body-bg);
  min-width: 9rem;
  max-width: 11rem;
}

.share-sub {
  font-size: 0.75rem;
  line-height: 1.2;
}

/* V and E side by side, each a tappable label rather than a bare box. */
.share-cell {
  display: inline-flex;
  gap: 0.5rem;
}

.share-cell label {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
  cursor: pointer;
}

.share-cell input {
  margin: 0;
}

.share-owner {
  font-size: 0.75rem;
  color: var(--cn-copper, var(--bs-secondary-color));
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* -- The tabular rows ----------------------------------------------------- */

/* Label left, value right, one rule between. Everything that reads as a table
   in this app is one of these. */
.jr-row {
  display: flex;
  /* Wraps rather than letting a long value squeeze the label to nothing. */
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.15rem 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--garage-line);
  line-height: 1.35;
}

.jr-row > * {
  min-width: 0;
  overflow-wrap: anywhere;
}

.jr-row:last-of-type {
  border-bottom: 0;
}

/* Specs: name, value, source, delete. Striped instead of ruled - at this
   density the stripes carry the eye across a row better than lines do.

   On a phone the value goes on its own line under the name. Sizing the value
   column `auto` let a long one ("Hyundai/Kia SP-IV or SP-IV-RR ATF") take
   max-content and crush the name to a single letter per line, so no column
   here is content-sized: the name always gets its share. */
.spec-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 2.6rem 1rem;
  grid-template-areas:
    "name  source delete"
    "value value  value";
  align-items: baseline;
  gap: 0.15rem 0.6rem;
  padding: 0.5rem;
  margin: 0 -0.5rem;
  border-radius: 0.35rem;
}

.spec-row > .spec-name { grid-area: name; }
.spec-row > .spec-value { grid-area: value; }
.spec-row > .spec-source { grid-area: source; }
.spec-row > form { grid-area: delete; }

/* Once there is room, back to one line with the values in a column. */
@media (min-width: 576px) {
  .spec-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) 2.6rem 1rem;
    grid-template-areas: "name value source delete";
    gap: 0.6rem;
  }

  .spec-row > .spec-value {
    text-align: right;
  }
}

.spec-row.jr-row {
  border-bottom: 0;
}

.spec-row:nth-of-type(even) {
  background: var(--garage-row-alt);
}

.spec-name {
  color: var(--garage-ink-muted);
  min-width: 0;
}

.spec-value {
  font-weight: 700;
  color: var(--garage-ink-strong);
  min-width: 0;
  overflow-wrap: anywhere;
}

.spec-source {
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  color: var(--garage-ink-muted);
  border: 1px solid var(--garage-line-strong);
  border-radius: 0.3rem;
  padding: 0.1rem 0.25rem;
  text-align: center;
}

.spec-section-head {
  cursor: pointer;
  list-style: none;
  color: var(--garage-ink-strong);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.85rem;
}

.spec-section-head::-webkit-details-marker {
  display: none;
}

.spec-section-head::before {
  content: "\25B6";
  font-size: 0.7em;
  margin-right: 0.5rem;
  color: var(--garage-ink-muted);
}

.spec-section[open] .spec-section-head::before {
  content: "\25BC";
}

.jr-warning {
  color: var(--garage-warning);
  font-size: 0.92rem;
}

/* Past maintenance groups are muted, never alarming (Rule 7). */
.group-past > summary {
  color: var(--garage-ink-muted);
}

.group-past {
  opacity: 0.72;
}

details.mileage-group {
  border-bottom: 1px solid var(--garage-line);
  padding: 0.55rem 0;
}

details.mileage-group > summary {
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}

details.mileage-group > summary::-webkit-details-marker {
  display: none;
}

details.mileage-group > summary::before {
  content: "\25B6";
  font-size: 0.7em;
  margin-right: 0.5rem;
  color: var(--garage-ink-muted);
}

details.mileage-group[open] > summary::before {
  content: "\25BC";
}

.match-yes { color: var(--garage-ok); }
.match-none { color: var(--garage-ink-muted); }

/* Cached OLP data: green edge so "already have it" reads at a glance and
   nobody spends requests they do not need to. */
.cached-card {
  border-color: var(--garage-cached-edge);
}

/* Pencil / red X row actions. Sized up for a thumb without adding visual
   weight to the list. */
.row-actions .btn-link {
  font-size: 1.15rem;
  line-height: 1;
  text-decoration: none;
  color: var(--garage-ink-muted);
  min-width: 28px;
}

.row-actions .btn-link:hover {
  color: var(--garage-ink-strong);
}

/* -- VIN capture ---------------------------------------------------------- */

/* Field, camera, Decode. One line where there is room; on a narrow phone the
   buttons drop below so all 17 characters stay visible at once - the whole
   point of the panel is checking them against the plate. */
.vin-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.vin-field {
  flex: 1 1 14rem;
  min-width: 0;
}

.vin-row .btn {
  flex: 1 1 auto;
}

/* Camera sits between the VIN field and Decode. Square and full height so it
   is a real target with a glove on, and never squeezes the field. The wrapper
   is the dropdown that offers camera or photo library. */
.vin-camera-wrap {
  flex: 0 0 auto;
  display: flex;
}

.btn-vin-camera {
  width: 58px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.btn-vin-camera svg {
  width: 24px;
  height: 24px;
}

/* Reading a plate takes a few seconds over the network, so the button itself
   has to show that something is happening. */
.vin-spinner {
  display: none;
}

.btn-vin-camera.is-busy svg {
  display: none;
}

.btn-vin-camera.is-busy .vin-spinner {
  display: inline-block;
}

/* Status lines that need to be noticed rather than skimmed. */
.is-notice {
  color: var(--garage-warning);
  font-weight: 600;
}

.is-working {
  color: var(--garage-link);
  font-weight: 600;
}

/* Job notes hold appended code assessments, which are many lines. */
.notes-block {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* A finished job's own figures: the same labels as the form that recorded
   them, with the answer under each instead of a field. Reading a record and
   filling one in should look like the same screen. */
.done-value {
  font-size: 1.05rem;
  color: var(--garage-ink-strong);
  line-height: 1.35;
}

.done-field + .row,
.done-field {
  margin-bottom: 0.25rem;
}

/* Code type-ahead. Served from the local table, so it can be instant. */
.code-matches {
  border: 1px solid var(--garage-line);
  border-radius: 0.5rem;
  overflow: hidden;
  background: var(--garage-card);
  box-shadow: var(--garage-shadow);
}

.code-match {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  border-bottom: 1px solid var(--garage-line);
  background: transparent;
  color: var(--garage-ink);
  padding: 0.55rem 0.75rem;
  font-size: 0.9rem;
  line-height: 1.3;
}

.code-match:last-child {
  border-bottom: 0;
}

.code-match:hover,
.code-match:focus {
  background: var(--garage-hover);
}

.code-match strong {
  color: var(--garage-ink-strong);
  margin-right: 0.5rem;
  font-variant-numeric: tabular-nums;
}

.code-confirm {
  margin: 0.5rem 0 0;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--garage-ok);
}

.code-confirm.is-unknown {
  color: var(--garage-ink-muted);
  font-weight: 400;
}

/* The composite read takes a few seconds, so the button says so. */
.diagnose-spinner {
  display: none;
}

#diagnose-run.is-busy .diagnose-spinner {
  display: inline-block;
  margin-right: 0.4rem;
}

.vin-plate-thumb {
  width: 56px;
  height: 42px;
  flex: 0 0 auto;
  object-fit: cover;
  border-radius: 0.4rem;
  border: 1px solid var(--garage-line);
}

/* Job title: reads as a heading, edits as a field. Driven by a data attribute
   so it renders correctly with JS disabled (a new job shows the input). */
.job-title[data-editing="0"] .js-title-input,
.job-title[data-editing="1"] .js-title-display {
  display: none;
}

/* -- Photos --------------------------------------------------------------- */

/* The hero is capped so a portrait shot cannot push the vehicle's actions
   below the fold on a phone. */
.vehicle-hero {
  width: 100%;
  max-height: 190px;
  object-fit: cover;
  border-radius: 0.75rem;
  border: 1px solid var(--garage-line);
  display: block;
}

/* Empty state doubles as the upload affordance, so the placeholder is not a
   dead end. */
.vehicle-hero-empty {
  height: 110px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  background: var(--garage-row-alt);
  border-style: dashed;
  color: var(--garage-ink-muted);
  text-decoration: none;
  font-size: 0.9rem;
}

.vehicle-hero-empty:hover {
  color: var(--garage-ink-strong);
  border-color: var(--garage-accent);
}

.vehicle-hero-empty .brand-icon {
  width: 1.75rem;
  height: 1.75rem;
  color: inherit;
}

/* Divider between the two ways to identify a vehicle. */
/* -- Add Vehicle, as three steps ------------------------------------------ */

/* Panels inside one form, shown one at a time. Hidden rather than unbuilt, so
   every field is in the DOM and the whole thing still posts once - there is no
   server-side half-finished vehicle to clean up. */
.wiz-step {
  display: none;
}

.wiz-step.is-on {
  display: block;
}

/* Where you are, in words. Three unlabelled dots say "three steps" but not
   which, and the names are short enough to fit a phone. */
.wiz-progress {
  display: flex;
  gap: 0.4rem;
  margin-bottom: 0.9rem;
}

.wiz-dot {
  flex: 1;
  padding-top: 0.45rem;
  border-top: 3px solid var(--garage-line);
  color: var(--garage-ink-faint);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.wiz-dot.is-on {
  border-top-color: var(--cn-red);
  color: var(--garage-ink-muted);
}

.wiz-nav {
  display: flex;
  gap: 0.6rem;
  margin-top: 1rem;
}

/* Back is secondary and narrow; the way on gets the rest of the row. */
.wiz-nav .btn {
  min-height: 48px;
}

.wiz-nav .btn-primary {
  flex: 1;
}

/* Square tiles, icon over label. A type is one tap rather than a dropdown to
   open, read and choose from - and four pictures are quicker to tell apart
   than four lines of text. */
.type-tiles {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.6rem;
}

.type-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  aspect-ratio: 1;
  padding: 0.75rem;
  background: var(--garage-card);
  border: 1px solid var(--garage-line);
  border-radius: 0.85rem;
  box-shadow: var(--garage-shadow);
  color: var(--garage-ink);
  text-align: center;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.type-tile:hover,
.type-tile:focus-visible {
  border-color: var(--garage-accent);
  background: var(--garage-hover);
  color: var(--garage-ink-strong);
  box-shadow: var(--garage-shadow-lift);
  transform: translateY(-1px);
}

/* The one that was picked stays obvious after the step moves on, for when
   somebody comes back to change it. */
.type-tile.is-chosen {
  border-color: var(--cn-red);
  box-shadow: var(--garage-shadow-lift);
}

.type-icon {
  width: 46%;
  max-width: 64px;
  height: auto;
  color: var(--garage-accent);
}

.type-tile.is-chosen .type-icon {
  color: var(--cn-red);
}

.type-tile-label {
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.2;
}

@media (prefers-reduced-motion: reduce) {
  .type-tile {
    transition: none;
  }

  .type-tile:hover,
  .type-tile:focus-visible {
    transform: none;
  }
}

.quick-or {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.1rem 0 0.9rem;
  color: var(--garage-ink-muted);
  font-size: 0.85rem;
}

.quick-or::before,
.quick-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--garage-line);
}

.vehicle-tile {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.vehicle-tile-text {
  min-width: 0;
}

/* The recall count sits on the corner of the photo. Red on the car itself,
   because a recall is the one thing on the home screen that can be a safety
   matter - and it is repeated as a word beside the mileage, since a coloured
   dot on its own is not something to rely on being read. */
.vehicle-thumb-wrap {
  position: relative;
  flex: 0 0 auto;
  line-height: 0;
}

.recall-dot {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  background: var(--cn-red);
  border: 2px solid var(--garage-card);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.15rem;
  text-align: center;
}

/* The darker brand red, 7.0:1 on a card. The raw red is 4.9:1 - fine for the
   badge, which is large and bold, but this is small type. Measured. */
.recall-note {
  color: var(--cn-red-hover);
  font-weight: 600;
}

[data-bs-theme="dark"] .recall-note {
  /* Either red is too dark to read on a dark card (3.1:1). This is 6.7:1 and
     still reads as the same red. Measured. */
  color: #ff8a8a;
}

.vehicle-thumb {
  width: 46px;
  height: 46px;
  flex: 0 0 auto;
  object-fit: cover;
  border-radius: 0.5rem;
  border: 1px solid var(--garage-line);
}

.vehicle-thumb-lg {
  width: 84px;
  height: 84px;
}

/* For a list row: tall enough to recognise the car, short enough that the
   row stays two lines on a phone. */
.vehicle-thumb-sm {
  width: 40px;
  height: 40px;
  border-radius: 0.4rem;
}

/* Vehicle screen: big enough to recognise the car, small enough that the
   title, mileage and actions all stay above the fold. */
.vehicle-thumb-xl {
  width: 104px;
  height: 78px;
  border-radius: 0.6rem;
}

.vehicle-thumb-add {
  flex-direction: column;
  gap: 0.15rem;
  border-style: dashed;
  font-size: 0.75rem;
  text-align: center;
  text-decoration: none;
  line-height: 1.1;
}

.vehicle-thumb-add:hover {
  color: var(--garage-ink-strong);
  border-color: var(--garage-accent);
}

/* Placeholder keeps tiles aligned whether or not a photo exists. */
.vehicle-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--garage-row-alt);
  color: var(--garage-ink-muted);
}

.vehicle-thumb-empty .brand-icon {
  width: 1.35rem;
  height: 1.35rem;
  color: inherit;
}

.photo-thumb {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 0.5rem;
  border: 1px solid var(--garage-line);
}

/* Where a pasted screen-grab lands. A dashed edge rather than a button,
   because there is nothing here to press - it is a place, not a control. */
.paste-box {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  align-items: center;
  justify-content: center;
  min-height: 64px;
  padding: 0.75rem;
  text-align: center;
  border: 2px dashed var(--garage-line-strong);
  border-radius: 0.6rem;
  background: var(--garage-row-alt);
}

.paste-box-title {
  font-weight: 600;
  color: var(--garage-ink);
}

.paste-box-hint {
  font-size: 0.85rem;
  color: var(--garage-ink-muted);
}

/* Confirms the paste arrived, which is otherwise only visible as a thumbnail
   further down the panel. */
.paste-box.is-ready {
  border-color: var(--garage-ok);
  border-style: solid;
}

.photo-queue {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.photo-queue:not(:empty) {
  margin-bottom: 0.5rem;
}

.photo-queued {
  width: 72px;
  height: 72px;
  object-fit: cover;
  border-radius: 0.45rem;
  border: 1px solid var(--garage-line-strong);
}

.chip {
  border-radius: 999px;
  font-size: 0.85rem;
  padding: 0.3rem 0.85rem;
}

/* The VIN, sized to be read off the screen and typed into NHTSA's tool. */
.vin-copy {
  padding: 0.3rem 0.5rem;
  background: var(--garage-row-alt);
  border: 1px solid var(--garage-line);
  border-radius: 0.4rem;
  color: var(--garage-ink-strong);
  font-size: 0.9rem;
  letter-spacing: 0.04em;
  user-select: all;
}

/* A plain count on a tab, for things that are not a warning. The recall badge
   is red because a recall can be a safety matter; a document is just a
   document. */
.vh-count {
  background: var(--garage-line-strong);
  color: var(--garage-ink-strong);
  font-size: 0.7rem;
  vertical-align: middle;
}

/* Steps and the kit list on a finished job. Tight enough to scan standing
   over an engine bay, with the marker outside the text so a wrapped line
   lines up under the words rather than under the number. */
.procedure-list {
  margin: 0;
  padding: 0.85rem 1rem 0.85rem 2.4rem;
  color: var(--garage-ink);
}

.procedure-list li {
  padding: 0.18rem 0;
  line-height: 1.4;
}

.procedure-list li::marker {
  color: var(--garage-ink-muted);
  font-weight: 600;
}

/* Bootstrap positions an alert's close button at top:0 with its own generous
   padding, which is sized for a full-height alert. These are `py-2`, so the
   cross ended up sitting low against a single line of text. Centred against
   whatever height the alert actually is. */
.alert-dismissible .btn-close {
  top: 50%;
  transform: translateY(-50%);
  padding: 0.75rem 1rem;
}

/* The formatting buttons over a text box. They write the markup into the box
   rather than hiding it in a widget: what is in the box is what is stored. */
.text-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-bottom: 0.3rem;
}

.text-tools .btn {
  min-width: 2.4rem;
  padding-block: 0.2rem;
  font-size: 0.8rem;
  line-height: 1.3;
}

/* A box dragged taller stays taller, and one asked to fit its contents can
   grow a long way - but not past the screen, or the save button goes with
   it. */
textarea[data-grow] {
  max-height: 70vh;
}

/* Appears only once something has been typed, so you are never hunting back
   up the page for a save button that was halfway down it. */
.save-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: none;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 1rem calc(0.7rem + env(safe-area-inset-bottom));
  background: var(--garage-card);
  border-top: 1px solid var(--garage-line-strong);
  box-shadow: var(--garage-shadow-lift);
}

.save-bar.is-on {
  display: flex;
}

.save-bar .save-bar-note {
  color: var(--garage-ink-muted);
  font-size: 0.85rem;
}

.save-bar .btn {
  margin-left: auto;
  min-height: 44px;
}

/* "Used last time": the parts previous jobs of this type actually used, with
   wherever they were bought. Set quieter than the typed kit list above it,
   because that list is what somebody decided and this is only what happened. */
.used-before {
  margin-top: 0.5rem;
  border: 1px dashed var(--garage-line-strong);
  border-radius: 0.6rem;
  padding: 0.5rem 0.7rem;
}

.used-before-head {
  color: var(--garage-ink-muted);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 0.2rem;
}

.used-before-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.3rem 0;
  border-bottom: 1px solid var(--garage-line);
}

.used-before-row:last-child {
  border-bottom: 0;
}

.used-before-name {
  display: block;
  font-weight: 600;
  color: var(--garage-ink);
}

.used-before-meta {
  display: block;
  color: var(--garage-ink-muted);
  font-size: 0.8rem;
}

/* The job's title and its save, on one row that stays put whatever tab is
   open. Wraps on a narrow phone rather than squeezing the title. */
.job-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}

.job-head .job-title {
  flex: 1 1 12rem;
  min-width: 0;
  margin-bottom: 0 !important;
}

.job-head .job-save {
  margin-left: auto;
  min-height: 44px;
  padding-inline: 1.3rem;
  font-weight: 600;
}

/* Field labels and panel headings are the two things you scan for, so both
   are bold. The panel heading keeps the muted ink: it names a region rather
   than a field, and at this weight and size it is plenty legible. */
.panel-title,
.section-label {
  font-weight: 800;
}

/* The + beside a reference figure, for copying it into the kit list. Quiet:
   it is a convenience next to the number, not a thing to notice first. */
/* A refusal, said in words rather than in an HTTP code. Centred and narrow,
   because there is one thing to read and two ways out. */
.refused {
  max-width: 28rem;
  margin: 2.5rem auto 0;
  text-align: center;
}

.refused-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  margin-bottom: 1rem;
  border-radius: 50%;
  background: var(--garage-row-alt);
  border: 1px solid var(--garage-line-strong);
  color: var(--garage-ink-muted);
}

.refused-mark svg,
.refused-mark .brand-icon {
  width: 1.6rem;
  height: 1.6rem;
  color: inherit;
}

.refused-why {
  color: var(--garage-ink);
  line-height: 1.5;
}

.refused-ways {
  margin-top: 1.5rem;
  text-align: left;
}

/* Painting over a page before it is sent. The cursor is a crosshair because
   the gesture is "drag across this", and touch panning is turned off on the
   page itself or a drag scrolls the screen instead of drawing. */
.redact-page-wrap {
  margin-bottom: 0.9rem;
}

.redact-page-label {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--garage-ink-muted);
  padding-bottom: 0.25rem;
}

.redact-page {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--garage-line-strong);
  border-radius: 0.4rem;
  cursor: crosshair;
  touch-action: none;
  background: #ffffff;
}

/* A heading inside a list, dividing two kinds of row - Due from Sometime.
   Quieter than a section label, which names a whole panel. */
.group-label {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--garage-ink-muted);
  padding: 0.6rem 0 0.15rem;
}

.group-label:first-child {
  padding-top: 0.2rem;
}

/* What else a visit covered. The type beside each line is the point: it says
   that item's reminder is handled, not just that the work happened. */
.task-list {
  list-style: none;
  padding: 0;
  margin: 0.35rem 0 0.6rem;
}

.task-row {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--garage-line);
}

.task-row:last-child {
  border-bottom: 0;
}

.task-name {
  font-weight: 600;
  color: var(--garage-ink-strong);
}

.task-type {
  font-size: 0.78rem;
  color: var(--garage-ink-muted);
  border: 1px solid var(--garage-line-strong);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
}

.task-drop {
  margin-left: auto;
}

/* Who did the work, as names. A stack of dropdowns each holding every name
   was the busiest thing on the screen for a usually one-word answer. */
.done-by-chosen {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.done-by-chosen:not(:empty) {
  margin: 0.35rem 0 0.5rem;
}

.who-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.5rem 0.3rem 0.7rem;
  border: 1px solid var(--garage-line-strong);
  border-radius: 999px;
  background: var(--garage-row-alt);
  font-size: 0.92rem;
}

.who-chip-name {
  font-weight: 600;
  color: var(--garage-ink-strong);
}

.who-chip-x {
  border: 0;
  background: none;
  padding: 0 0.15rem;
  line-height: 1;
  font-size: 1.1rem;
  color: var(--garage-ink-muted);
}

.who-chip-x:hover {
  color: var(--cn-red);
}

/* What a photo shows, under its thumbnail. Quiet and borderless until
   touched, so a grid of pictures stays a grid of pictures rather than a form
   with pictures in it. */
.photo-caption .form-control {
  border-color: transparent;
  background: transparent;
  padding-inline: 0.25rem;
  font-size: 0.8rem;
  color: var(--garage-ink);
}

.photo-caption .form-control:hover {
  border-color: var(--garage-line);
}

.photo-caption .form-control:focus {
  border-color: var(--garage-input-border);
  background: var(--garage-input-focus);
}

.photo-caption-text {
  font-size: 0.8rem;
  color: var(--garage-ink-muted);
  padding-inline: 0.25rem;
}

/* The parts already on a job. Set larger than the usual list row: it is the
   thing the tab is for, and it used to be the smaller half of a panel whose
   other half was a form. */
.parts-fitted .upcoming-title {
  font-size: 1.08rem;
  line-height: 1.3;
}

.parts-fitted .upcoming-item {
  padding-block: 0.6rem;
}

/* Seven fields for a part nobody has fitted before, folded away until asked
   for. A `details` rather than a scripted panel, so it works with JavaScript
   off - the rest of this tab does. */
.add-part > summary {
  list-style: none;
  cursor: pointer;
  display: block;
  padding: 0.6rem 0.9rem;
  border: 1px dashed var(--garage-line-strong);
  border-radius: 0.5rem;
  text-align: center;
  font-weight: 600;
  color: var(--garage-link);
  background: var(--garage-row-alt);
}

.add-part > summary::-webkit-details-marker {
  display: none;
}

.add-part > summary:hover {
  background: var(--garage-hover);
}

.add-part[open] > summary {
  border-style: solid;
  border-radius: 0.5rem 0.5rem 0 0;
}

.jr-copy {
  font-size: 1.05rem;
  line-height: 1;
  color: var(--garage-link);
  text-decoration: none;
}

.jr-copy:hover {
  color: var(--garage-ink-strong);
}
