/* Grappler USA. Industrial, high contrast, readable on a phone in daylight. */

:root {
  --ink: #14181c;
  --ink-soft: #4a555f;
  --faint: #7c8894;
  --paper: #f4f4f2;
  --panel: #fff;
  --line: #d8d8d3;
  --steel: #1f2a33;
  --steel-deep: #131b21;
  --hi: #f0a202;      /* safety yellow */
  --hi-deep: #c07f00;
  --go: #2f6f4f;
  --stop: #a8321f;
  --wrap: 66rem;
}

* { box-sizing: border-box; }
body {
  margin: 0; background: var(--paper); color: var(--ink);
  font: 400 17px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
h1,h2,h3 { font-weight: 800; line-height: 1.12; margin: 0 0 .5em; letter-spacing: -0.02em; color: var(--steel-deep); }
h1 { font-size: clamp(2rem,5vw,3.1rem); text-transform: uppercase; }
h2 { font-size: clamp(1.4rem,3vw,2rem); margin-top: 1.8em; text-transform: uppercase; }
h3 { font-size: 1.15rem; margin-top: 1.4em; }
p, li { max-width: 36rem; }
p { margin: 0 0 1em; }
a { color: var(--steel); text-underline-offset: 2px; }
a:hover { color: var(--hi-deep); }
img { max-width: 100%; height: auto; }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 1.1rem; }
.muted { color: var(--ink-soft); }
.small { font-size: .87rem; }
.lead { font-size: 1.18rem; color: var(--ink-soft); max-width: 40rem; }

.banner { padding: .55rem 1rem; text-align: center; font-size: .88rem; font-weight: 600; }
.banner.test { background: #fff4d6; color: #6b4c00; }
.banner.warn { background: #fde8e3; color: #7a2718; }

/* header */
header.site { background: var(--steel-deep); position: sticky; top: 0; z-index: 20; }
header.site .bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; min-height: 62px; }
/* Larger than it was, at 44px. The mark is the only colour in a dark bar and
   it was being out-shouted by the Cart button next to it. */
.brand img { height: 44px; display: block; }
header.site nav { display: flex; gap: 1.1rem; align-items: center; flex-wrap: wrap; }
header.site nav a { color: #c3ccd4; text-decoration: none; font-size: .92rem; font-weight: 600; }
header.site nav a:hover { color: #fff; }
/*
 * Yellow means two things in this bar and no more: the mark, and Call Now.
 *
 * It used to mean four, which is the same as meaning nothing. Contact Us is a
 * normal link now, and the cart is a quiet outline until there is something in
 * it, at which point it earns the colour back.
 */
header.site nav a.cart {
  padding: .42rem .8rem; border-radius: 3px;
  border: 1px solid rgba(255,255,255,.28);
}
header.site nav a.cart:hover { border-color: #fff; color: #fff; }
header.site nav a.cart.has-items { background: var(--hi); color: #191919; border-color: var(--hi); }
header.site nav a.cart.has-items:hover { background: #ffb51f; color: #191919; }

/* Call now. Outlined rather than filled, so it reads as a button without
   competing with the cart, which is the one filled control in the bar. */
header.site nav a.call {
  color: var(--hi); border: 1.5px solid var(--hi); border-radius: 3px;
  padding: .32rem .7rem; font-weight: 700; white-space: nowrap;
}
header.site nav a.call:hover { background: var(--hi); color: #191919; }
.pill { background: #191919; color: var(--hi); border-radius: 2px; padding: 0 .32rem; font-size: .8rem; }

/* sections */
section { padding: 3rem 0; }
section.tight { padding: 1.6rem 0; }
section.steel { background: var(--steel-deep); color: #ccd5dc; }
section.steel h1, section.steel h2, section.steel h3 { color: #fff; }
section.band { background: var(--panel); border-block: 1px solid var(--line); }

.eyebrow { text-transform: uppercase; letter-spacing: .12em; font-size: .74rem; font-weight: 800; color: var(--hi-deep); margin: 0 0 .7rem; }
section.steel .eyebrow { color: var(--hi); }

.hero { display: grid; grid-template-columns: 1.1fr .9fr; gap: 2.5rem; align-items: center; }
.hero img { border: 1px solid var(--line); background: #fff; }

.grid { display: grid; gap: 1.4rem; }
.grid.two { grid-template-columns: repeat(auto-fit,minmax(19rem,1fr)); }
.grid.three { grid-template-columns: repeat(auto-fit,minmax(15rem,1fr)); }
.grid p, .grid li { max-width: none; }

.card { background: var(--panel); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; display: flex; flex-direction: column; }
.card .pad { padding: 1.1rem 1.2rem 1.3rem; display: flex; flex-direction: column; flex: 1; }
.card .shot { background: #fff; border-bottom: 1px solid var(--line); display: grid; place-items: center; padding: 1rem; min-height: 190px; }
.card .shot img { max-height: 190px; width: auto; }
.card h3 { margin: 0 0 .3rem; }
.card .push { margin-top: auto; padding-top: .9rem; }

.tag { display: inline-block; font-size: .68rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; padding: .2rem .45rem; border-radius: 2px; background: var(--line); color: var(--ink-soft); }
.tag.soon { background: #e7edf3; color: var(--steel); }
.tag.spec { background: #fff4d6; color: #6b4c00; }

.price { font-size: 1.5rem; font-weight: 800; color: var(--steel-deep); }
.price.tbd { font-size: 1rem; font-weight: 700; color: var(--ink-soft); }

ul.checks { list-style: none; padding: 0; }
ul.checks li { position: relative; padding-left: 1.5rem; margin-bottom: .5rem; }
ul.checks li::before { content: "▸"; position: absolute; left: 0; color: var(--hi-deep); font-weight: 900; }

ol.steps { padding-left: 1.2rem; }
ol.steps li { margin-bottom: .7rem; }

.btn { display: inline-block; background: var(--hi); color: #191919; padding: .8rem 1.4rem; border: 0; border-radius: 3px; font: inherit; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; cursor: pointer; text-decoration: none; }
.btn:hover { background: #ffb51f; color: #191919; }
.btn.big { padding: 1rem 1.8rem; font-size: 1.05rem; }
.btn.ghost { background: transparent; border: 2px solid var(--steel); color: var(--steel); }
.btn.ghost:hover { background: var(--steel); color: #fff; }
.btn:disabled { background: var(--line); color: var(--faint); cursor: not-allowed; }
.btn-row { display: flex; gap: .7rem; flex-wrap: wrap; align-items: center; margin: 1.4rem 0; }

/* Off screen but still focusable and still the form's default submit button. */
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* Taking a line out of the basket. Quiet on purpose: it sits next to every
   quantity box and should never compete with Check out. */
.remove { display: inline-block; margin-top: .4rem; padding: 0; background: none; border: 0; font: inherit; font-size: .82rem; color: var(--ink-soft); text-decoration: underline; cursor: pointer; }
.remove:hover, .remove:focus-visible { color: #b3261e; }

table { width: 100%; border-collapse: collapse; margin: 1.2rem 0; }
th, td { text-align: left; padding: .7rem .6rem; border-bottom: 1px solid var(--line); vertical-align: top; }
th { font-size: .74rem; text-transform: uppercase; letter-spacing: .07em; color: var(--faint); }
td.right, th.right { text-align: right; }
.scroll { overflow-x: auto; }

label.field { display: block; margin-bottom: 1rem; max-width: 30rem; }
label.field span { display: block; font-weight: 700; font-size: .9rem; margin-bottom: .25rem; }
input[type=text],input[type=email],input[type=tel],input[type=number],select,textarea {
  width: 100%; padding: .6rem .7rem; font: inherit; border: 1.5px solid var(--line); border-radius: 3px; background: #fff;
}
input:focus,select:focus,textarea:focus { outline: 2px solid var(--hi); outline-offset: 1px; border-color: var(--hi-deep); }
textarea { min-height: 7rem; resize: vertical; }
input.qty { width: 5rem; }

.note { background: #fff4d6; border-left: 4px solid var(--hi); padding: 1rem 1.1rem; margin: 1.4rem 0; }
.note p:last-child { margin-bottom: 0; }
.errors { background: #fde8e3; border-left: 4px solid var(--stop); padding: 1rem 1.1rem; margin-bottom: 1.5rem; }
.flash { background: #e8f3ec; border-left: 4px solid var(--go); padding: 1rem 1.1rem; margin-bottom: 1.5rem; }

.tiles { display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit,minmax(9rem,1fr)); margin-bottom: 1.6rem; }
.tile { background: var(--panel); border: 1px solid var(--line); padding: .9rem 1rem; border-radius: 4px; }
.tile .n { font-size: 1.6rem; font-weight: 800; color: var(--steel-deep); }
.tile .l { font-size: .74rem; text-transform: uppercase; letter-spacing: .06em; color: var(--faint); }

footer.site { background: var(--steel-deep); color: #a9b4be; margin-top: 3rem; padding: 2.5rem 0 1.2rem; }
footer.site a { color: #fff; }
footer.site p { max-width: 20rem; margin: 0 0 .3rem; }
.foot { display: grid; gap: 1.8rem; grid-template-columns: repeat(auto-fit,minmax(12rem,1fr)); }
.fname { font-weight: 800; color: #fff; font-size: 1.05rem; text-transform: uppercase; }
.fhead { font-size: .72rem; text-transform: uppercase; letter-spacing: .1em; color: #7e8b97; margin-bottom: .5rem !important; }
.legal { border-top: 1px solid #2a3640; margin-top: 2rem; padding-top: 1rem; }
footer.site .muted { color: #7e8b97; }

@media (max-width: 720px) {
  .hero { grid-template-columns: 1fr; gap: 1.4rem; }
}

/*
 * The phone header.
 *
 * It was 131px, and the product bar under it another 66, so a quarter of an
 * 812px screen was chrome before a word of the page. The 44px mark is right on
 * a desktop, where it is the only colour in a wide dark bar. On a 375px screen
 * it is half the width of the device, and the nav wrapped to two rows beneath
 * it.
 *
 * So: a smaller mark, a tighter bar, and smaller type in the nav. The nav
 * still wraps to two rows, which is honest. Seven destinations do not fit on
 * one row of a phone, and hiding some of them to pretend otherwise would cost
 * more than the pixels are worth.
 */
@media (max-width: 700px) {
  /*
   * Tried and rejected: nav beside the mark, with flex: 1 1 0 so it shrinks
   * onto the logo's line. It saved 11px and cost a third wrapped row, leaving
   * the Cart alone on the end of a 246px column. Two clean rows under a short
   * mark reads better than three cramped ones next to it.
   */
  header.site .bar { min-height: 0; gap: .5rem; padding-block: .4rem; }
  .brand img { height: 26px; }
  /* Two rows, but tight ones: the row gap is what made it look like a block
     rather than a bar. */
  /* About and the account link come out of the nav here. With them gone the
     remaining five fit one row, and both are in the footer. */
  header.site nav a.not-on-phones { display: none; }
  header.site nav { gap: .3rem .6rem; row-gap: .3rem; }
  header.site nav a { font-size: .74rem; }
  header.site nav a.call,
  header.site nav a.cart { padding: .2rem .45rem; }
  header.site nav a.cart .pill { margin-left: .2rem; }
}

/*
 * Narrow phones: 360px Androids, and a folded Galaxy Z Fold at 344.
 *
 * The five links left at 700px want 329px of row. After the mark, a 344px bar
 * offers about 190. Tightening gaps and padding recovers perhaps 30 of that,
 * so the arithmetic never closes: two links have to go, and shrinking the type
 * far enough to keep them would leave a tap target nobody can hit from a
 * truck.
 *
 * Dealers and Contact Us are the two that go, and Call Now stays, because a
 * contractor holding a phone wants to ring rather than read a form. Both are
 * in the footer.
 */
/*
 * The narrowest of them, around 320px: the original Galaxy Z Fold's cover
 * screen, and the old small iPhones.
 *
 * Three links still want 209px of a row that offers 201, so one more goes. It
 * is All Products, and it is the right one: the sticky bar immediately below
 * lists all six products by name, so the catalogue is already one tap away and
 * the link duplicates it. Call Now and the basket cannot be duplicated by
 * anything, so they stay.
 *
 * Shrinking the mark instead was the alternative, and it was rejected: Ryan
 * asked for a larger logo, not a smaller one, and 18px of wordmark to save one
 * redundant link is a bad trade.
 */
@media (max-width: 340px) {
  header.site nav a.not-on-tiny { display: none; }
}

@media (max-width: 374px) {
  header.site nav a.not-on-narrow { display: none; }
  /* The mark is a third of the bar at this width, so it gives some back. */
  .brand img { height: 22px; }
  /* 1.1rem of gutter either side is a lot of a 344px screen. */
  header.site .bar { padding-inline: .6rem; }
  header.site nav { gap: .3rem .5rem; }
}

.note.warn { background:#fdeeeb; border-left-color: var(--stop); }

/* Product gallery. Ryan's own field photographs, added 2026-08-31. */
.gallery { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit,minmax(15rem,1fr)); margin: 0; }
.gallery figure { margin: 0; background: var(--panel); border: 1px solid var(--line); border-radius: 4px; overflow: hidden; }
.gallery img { display: block; width: 100%; height: 210px; object-fit: cover; background: #fff; }
/* Drawings and spec art get letterboxed rather than cropped. */
.gallery img.contain { object-fit: contain; padding: .5rem; }
.gallery figcaption { padding: .65rem .8rem .75rem; font-size: .87rem; color: var(--ink-soft); }
.docs { list-style: none; padding: 0; margin: 1.2rem 0 0; }
.docs li { margin-bottom: .4rem; }

/* Quotation-style breakdown, shared shape with the Prairie catalogue. Sub-rows
   explain the figure above them, so they are indented and quiet. */
table.totals th { text-align: left; font-weight: 600; }
table.totals tr.sub td:first-child { padding-left: 1.25rem; }
table.totals tr.sub td { border-bottom: 0; padding-top: .15rem; padding-bottom: .15rem; }
table.totals tr.total th,
table.totals tr.total td { border-top: 2px solid currentColor; }

/* ------------------------------------------------- Ryan's feedback, 5 Sep */

/* Note under a product photograph, for the 3-leg lifter that is pictured by
   its 2-leg sibling. Better labelled than passed off. */
.imgnote { margin-top: .4rem; }

/* Full product list. */
.prod-index { display: flex; flex-direction: column; gap: .6rem; margin-top: 1.4rem; }
.prod-row {
  display: grid; grid-template-columns: 84px 1fr auto; gap: 1rem; align-items: center;
  padding: .9rem 1rem; text-decoration: none; color: inherit;
  background: var(--panel); border: 1px solid var(--line); border-radius: 3px;
}
.prod-row:hover { border-color: var(--steel); }
.prod-row img { width: 84px; height: 84px; object-fit: contain; }
.prod-row-body h2 { margin: 0 0 .2rem; font-size: 1.05rem; }
.prod-row-body p { margin: 0; }
.prod-row-price { text-align: right; display: flex; flex-direction: column; gap: .15rem; white-space: nowrap; }

/*
 * The product row, stacked, on anything phone sized.
 *
 * "84px 1fr auto" keeps three columns at every width, and the price column
 * carries white-space: nowrap so it will not give any of itself back. On a
 * 344px screen that leaves the description about 50px to live in, which wraps
 * it to two words a line, and the price runs off the right edge entirely.
 *
 * So below 560px the price drops to its own line under the name, where it has
 * the full width and reads as a price rather than as something falling off the
 * card. The volume break sits beside it rather than under it, because on one
 * line the two belong together.
 */
@media (max-width: 560px) {
  .prod-row { grid-template-columns: 64px 1fr; gap: .8rem; padding: .8rem; }
  .prod-row img { width: 64px; height: 64px; }
  .prod-row-price {
    grid-column: 1 / -1;
    flex-direction: row; align-items: baseline; gap: .5rem;
    text-align: left; white-space: normal;
  }
}

/* Embedded video. */
.video { position: relative; aspect-ratio: 16 / 9; max-width: 48rem; border-radius: 3px; overflow: hidden; background: var(--steel-deep); }
.video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* Related products. */
/* The "Often ordered with this" cards are gone, replaced by .kits below.
   Nothing renders .rel-card any more. */

/* Sticky buy bar, phone only. */
.buybar { display: none; }
@media (max-width: 700px) {
  .buybar {
    position: sticky; bottom: 0; z-index: 20;
    display: flex; align-items: center; gap: .6rem;
    padding: .6rem .9rem; margin: 0;
    background: var(--steel); color: #fff;
    box-shadow: 0 -6px 18px rgba(0,0,0,.18);
  }
  .buybar-name {
    flex: 1 1 auto; min-width: 0; font-size: .85rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .buybar-price { font-weight: 700; white-space: nowrap; }
  .buybar .btn { white-space: nowrap; }
}
@media print { .buybar { display: none; } }

/* Checkout: who is buying, and the exemption claim. */
fieldset.buyer, fieldset.exempt { border: 1px solid var(--line); border-radius: 3px; padding: .9rem 1rem; margin: 0 0 1rem; }
fieldset.buyer legend, fieldset.exempt legend { padding: 0 .4rem; font-weight: 700; font-size: .9rem; }
.choices { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .4rem; margin-top: .5rem; }
.choice, .check { display: flex; align-items: center; gap: .5rem; padding: .5rem .6rem; border: 1px solid var(--line); border-radius: 3px; cursor: pointer; }
.choice:hover, .check:hover { border-color: var(--steel); }
.exempt-fields { margin-top: .8rem; }
.exempt-fields[hidden] { display: none; }
tr.hot td { background: rgba(240,162,2,.10); }

/* ------------------------------------------------------------- product bar
 *
 * Every product as a thumbnail row, directly beneath the navigation.
 *
 * It is a sibling of <header>, not a child. Nesting it made the desktop case
 * trivial, because the header is already sticky and the two moved as one, but
 * it made every other case impossible: a sticky child only sticks inside its
 * parent, so the bar could never outlive the header.
 */
.product-bar { background: var(--steel); border-top: 1px solid rgba(255,255,255,.08); }

/*
 * Three groups across, each a pair of products over a caption.
 *
 * The bar got taller to make room for the captions, which is what Ryan did in
 * his mockup and the reason he did it. The groups share the width evenly
 * rather than the products doing so, so a pair stays visibly a pair and the
 * caption underneath has an obvious span.
 */
.product-bar-inner { display: flex; gap: .4rem; align-items: stretch; }
.product-bar-group { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; }
.product-bar-row { display: flex; gap: .4rem; align-items: stretch; flex: 1 1 auto; }

/* The caption names the pair above it and goes to that category. It reads as
   a heading, so it has to look like something you can click or it reads as a
   dead label sitting over a row of links. */
.product-bar-caption {
  display: block;
  margin: 0; padding: .15rem .3rem .3rem;
  font-size: .62rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  text-align: center; color: #8d99a4; line-height: 1.2;
  border-top: 1px solid rgba(255,255,255,.07);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-decoration: none;
}
a.product-bar-caption:hover,
a.product-bar-caption:focus-visible { color: var(--hi); background: rgba(255,255,255,.05); }

.product-bar-item {
  flex: 1 1 0;
  display: flex; flex-direction: column; align-items: center; gap: .3rem;
  padding: .5rem .3rem .45rem;
  text-decoration: none; color: #c3ccd4;
  border-bottom: 3px solid transparent;
  min-width: 0;
}
.product-bar-item:hover { color: #fff; background: rgba(255,255,255,.06); }
.product-bar-item.is-current { color: #fff; border-bottom-color: var(--hi); background: rgba(255,255,255,.08); }

/* No white plate. Every cut-out product now carries its own transparency, so
   the tools sit on the steel instead of on six white tiles. The two heroes
   that are photographs rather than cut-outs keep a rounded frame, which reads
   as a photograph on purpose rather than as a tile nobody removed. */
.product-bar-shot {
  width: 44px; height: 44px; border-radius: 4px; overflow: hidden;
  display: flex; align-items: center; justify-content: center; padding: 2px;
}
.product-bar-shot img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.product-bar-shot.is-photo { padding: 0; background: rgba(255,255,255,.09); }
.product-bar-shot.is-photo img { width: 100%; height: 100%; max-width: none; max-height: none; object-fit: cover; }

.product-bar-label {
  font-size: .7rem; font-weight: 700; letter-spacing: .02em; line-height: 1.15;
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}

/*
 * Sticky only where the nav is reliably a single 62px row.
 *
 * Below this the nav wraps, and wrapping changes the height the bar would have
 * to be offset by. Rather than guess that height, or add JavaScript to measure
 * it on a site that carries none, the bar simply scrolls at narrower widths.
 * A bar sitting 20px under the header is worse than one that scrolls.
 */
@media (min-width: 1000px) {
  /* 62px is the nav's height, which is what this sits under. The bar's own
     height changed with the captions, but the offset is the HEADER's and so
     it did not. */
  .product-bar { position: sticky; top: 62px; z-index: 19; }
}

/*
 * Phones keep the tiles legible and scroll the row sideways instead of
 * shrinking six of them into a 375px screen.
 *
 * Not sticky here, and not pinned to the bottom either: product pages already
 * carry a fixed Add to cart bar along the bottom edge, and two competing bars
 * is worse than none. The header remains sticky exactly as it was, so nothing
 * that worked before is lost.
 */
@media (max-width: 700px) {
  .product-bar-inner {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    gap: 0;
  }
  .product-bar-inner::-webkit-scrollbar { display: none; }
  /* The GROUP is what snaps and what scrolls, so a pair and its caption never
     come apart across the edge of the screen. */
  .product-bar-group { flex: 0 0 auto; scroll-snap-align: start; }
  .product-bar-row { gap: 0; }
  .product-bar-item {
    flex: 0 0 auto;
    width: 4.2rem;
    padding: .3rem .2rem .25rem;
    border-bottom-width: 2px;
    gap: .2rem;
  }
  .product-bar-shot { width: 28px; height: 28px; }
  .product-bar-label { font-size: .58rem; }
  .product-bar-caption { font-size: .5rem; letter-spacing: .02em; padding: .1rem .2rem .25rem; }
}

/* ----------------------------------------------------------------- contact
 *
 * The number and the address sit above the form. Somebody who came here to
 * telephone should not have to scroll past a form to find the number.
 */
.contact-direct {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem; margin: 1.4rem 0 2rem;
}
.contact-direct-item {
  display: flex; flex-direction: column; gap: .2rem;
  padding: 1rem 1.1rem; background: #fff; border: 1px solid var(--line); border-radius: 4px;
}
.contact-direct-label {
  font-size: .72rem; font-weight: 800; text-transform: uppercase; letter-spacing: .08em;
  color: var(--faint);
}
.contact-direct-value { font-size: 1.15rem; font-weight: 800; color: var(--steel-deep); text-decoration: none; }
a.contact-direct-value:hover { text-decoration: underline; }
.contact-direct-value.plain { font-weight: 700; }

.contact-form .field.wide { max-width: none; }
.contact-form .field span em { font-style: normal; color: var(--faint); font-weight: 600; font-size: .8em; }

/* The preferred-channel choice. Radios, not a select: four options are worth
   showing at once, and a select hides them behind a tap. */
/* Named choice-group, not choice: .choice already exists for a single
   radio option, and a fieldset matching it became a flex row by accident. */
fieldset.choice-group { border: 0; padding: 0; margin: .4rem 0 1.2rem; display: block; }
fieldset.choice-group legend { font-weight: 700; font-size: .9rem; margin-bottom: .5rem; padding: 0; }
.choice-option {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .5rem .8rem; margin: 0 .5rem .5rem 0;
  border: 1.5px solid var(--line); border-radius: 3px; background: #fff;
  cursor: pointer; font-size: .92rem;
}
.choice-option:hover { border-color: var(--steel); }
.choice-option input { margin: 0; }
.choice-option:has(input:checked) { border-color: var(--steel); background: #eef2f5; font-weight: 700; }

.choice-option.wide { display: flex; width: 100%; max-width: 34rem; align-items: flex-start; margin-right: 0; }
.choice-option.wide span { display: flex; flex-direction: column; gap: .1rem; }
.choice-option.wide .muted { font-weight: 400; }

@media (max-width: 700px) {
  .choice-option { display: flex; margin-right: 0; }
}

/* -------------------------------------------------------- city code quotes
 *
 * Real excerpts from municipal specifications. Set as quotations rather than
 * body copy, because the point is that these are somebody else's words.
 */
.codes { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: 1rem; margin: 1.2rem 0 1rem; }
.code-quote { margin: 0; background: #fff; border: 1px solid var(--line); border-left: 4px solid var(--hi); border-radius: 4px; padding: 1rem 1.1rem; }
.code-quote blockquote { margin: 0; }
.code-quote blockquote p { margin: 0 0 .6rem; font-size: .93rem; line-height: 1.5; }
.code-quote blockquote p:last-child { margin-bottom: .8rem; }
.code-quote figcaption { font-size: .8rem; line-height: 1.35; border-top: 1px solid var(--line); padding-top: .6rem; }
.code-quote figcaption strong { display: block; }

/* The basket echoed back on the quotation form, so nobody has to retype what
   they already put in the cart. */
.basket-lines { margin: 0; padding-left: 1.1rem; }
.basket-lines li { margin: .15rem 0; }
.basket-lines .muted { margin-left: .35rem; }

/* Kits. Two cards side by side on a desktop, stacked on a phone. The
   "choose this when" line is the one people read, so it is not muted. */
.kits { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.1rem; margin: 1rem 0; }
.kit {
  background: #fff; border: 1px solid var(--line); border-radius: 6px;
  padding: 1.1rem 1.2rem; display: flex; flex-direction: column;
}
.kit h3 { margin: 0 0 .4rem; }
.kit-when { margin: 0 0 .5rem; font-size: .92rem; }
.kit-parts { list-style: none; margin: .8rem 0; padding: 0; }
.kit-parts li {
  display: flex; align-items: center; gap: .6rem;
  padding: .35rem 0; border-top: 1px solid var(--line); font-size: .9rem;
}
.kit-parts li img { width: 34px; height: 34px; object-fit: contain; flex: 0 0 auto; }
.kit-parts li span:nth-of-type(1) { flex: 1 1 auto; min-width: 0; }
.kit-parts li a { text-decoration: none; }
.kit-parts li a:hover { text-decoration: underline; }
.kit-price { font-size: 1.3rem; font-weight: 700; margin: auto 0 .7rem; }
.kit-price .muted { font-weight: 400; margin-left: .4rem; }
.kit .btn-row { margin: 0; }
@media (max-width: 760px) {
  .kits { grid-template-columns: 1fr; }
}

/* The panels under the hero are plain text in a steel band, styled by
   section.steel and .grid.three. They were briefly boxed links; nothing
   renders .route any more. */

/* The three claims above the copyright line. Yellow rules rather than yellow
   fills: this is the foot of the page, and the brand colour belongs to the
   mark and to Call Now, not to a row of badges. */
.marks { display: flex; flex-wrap: wrap; gap: .5rem .9rem; padding-top: 1.2rem; }
.marks .mark {
  font-size: .72rem; font-weight: 800; text-transform: uppercase; letter-spacing: .09em;
  color: #9fb0bd; border-left: 3px solid var(--hi); padding-left: .55rem; text-decoration: none;
}
.marks a.mark:hover { color: #fff; }

/* ------------------------------------------------------------------- admin
 *
 * The dashboard, folded up. Approvals at the top, the price table at the
 * bottom, and everything shut until it is wanted.
 */
details.adm { border: 1px solid var(--line); border-radius: 6px; background: #fff; margin: .7rem 0; }
details.adm > summary {
  list-style: none; cursor: pointer; padding: .85rem 1rem;
  display: flex; align-items: center; gap: .6rem;
  font-weight: 800; font-size: 1.05rem;
}
details.adm > summary::-webkit-details-marker { display: none; }
/* A triangle that turns, drawn in CSS so no icon font is needed. */
details.adm > summary::before {
  content: ""; flex: 0 0 auto; width: 0; height: 0;
  border-left: 6px solid var(--steel); border-top: 5px solid transparent; border-bottom: 5px solid transparent;
  transition: transform .12s ease;
}
details.adm[open] > summary::before { transform: rotate(90deg); }
details.adm > summary:hover { background: var(--panel); }
details.adm[open] > summary { border-bottom: 1px solid var(--line); }
.adm-title { display: inline-flex; align-items: center; gap: .5rem; }
.adm-body { padding: .2rem 1rem 1.1rem; }
/* The section headings are gone; what is left inside should not shout. */
.adm-body h3 { margin-top: 1.2rem; }
@media print { details.adm { border: 0; } details.adm > summary { display: none; } }

/* A submit button that reads as a link, for an action inside a sentence. */
.linky {
  background: none; border: 0; padding: 0; font: inherit; color: var(--hi-deep);
  text-decoration: underline; cursor: pointer;
}
.linky:hover { color: var(--steel-deep); }

/* The explanation under a designation checkbox on the register form. Indented
   to the checkbox's label rather than the form edge, so it reads as belonging
   to the box above it. */
.designation-help { margin: .25rem 0 .75rem .6rem; }

/* Why entries matter. Four government figures, sized so the number reads first
   and the citation is still there for somebody who checks. Same card treatment
   as the city specification quotes, so the two sections read as a pair. */
.entry-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); gap: 1rem; margin: 1.4rem 0 1.6rem; }
.entry-stat { margin: 0; background: #fff; border: 1px solid var(--line); border-left: 4px solid var(--hi); border-radius: 4px; padding: 1rem 1.1rem; }
.entry-stat-figure { display: block; font-size: 2.4rem; line-height: 1; font-weight: 800; color: var(--hi-deep); }
.entry-stat-label { margin: .45rem 0 .5rem; font-weight: 700; font-size: .95rem; line-height: 1.35; }
.entry-stat-detail { margin: 0 0 .8rem; font-size: .85rem; line-height: 1.5; color: var(--ink-soft); }
.entry-stat figcaption { font-size: .76rem; line-height: 1.35; border-top: 1px solid var(--line); padding-top: .6rem; }
.entry-steps { max-width: 44rem; margin: .8rem 0 1.2rem; padding-left: 1.2rem; }
.entry-steps li { margin: .35rem 0; line-height: 1.5; }

/*
 * Admin buttons.
 *
 * .btn.small and .linkish were used throughout the admin and never defined,
 * so every "small" button rendered at full size and every "linkish" one as a
 * bare browser button. Defined here, with the two states the promo code list
 * needed: a toggle that shows whether a code works, and a delete that looks
 * like it means it.
 */
.btn.small { padding: .38rem .75rem; font-size: .78rem; letter-spacing: .03em; }
.btn.on { background: #1f7a3d; color: #fff; }
.btn.on:hover { background: #185f30; color: #fff; }
.btn.danger { border-color: #9b2c2c; color: #9b2c2c; }
.btn.danger:hover { background: #9b2c2c; color: #fff; }
.linkish {
  background: none; border: 0; padding: 0; font: inherit; color: var(--hi-deep);
  text-decoration: underline; cursor: pointer;
}
.linkish:hover { color: var(--steel-deep); }
.nowrap { white-space: nowrap; }
td.nowrap form.inline + form.inline { margin-left: .35rem; }
/* A new lead or enquiry in the queue, same amber as a new row in a table. */
.card.hot { border-left: 4px solid var(--hi); background: rgba(240,162,2,.06); }
/* The pricing grids: small inputs, right aligned, so a column of prices reads
   like a column of prices rather than a column of text boxes. */
.grid-prices td, .grid-prices th { vertical-align: top; }
.price-cell { width: 6.5rem; text-align: right; padding: .3rem .4rem; font: inherit; }
.price-cell.pct { width: 4rem; }
.price-cell::placeholder { color: var(--faint, #999); }
.sheet-products { border: 1px solid var(--line); border-radius: 4px; padding: .5rem .9rem; margin: .6rem 0; }
.sheet-products legend { padding: 0 .3rem; }
