The 2 files this component needs and nothing else, each fenced with the path it belongs at, followed by what the library's own verification covered and what it did not. No instructions attached — for placing it yourself, or handing it to something that has its own.
Read it first
Install kit: "Shipment quote form"
Library page: https://astro.baysixmedia.com/components/shipment-quote-form/
Files: 2 required, 1 supporting
Write each block below to the path named in its header, relative to the project root. A block marked (required) is one the component does not work without; (supporting) is documentation. Nothing here needs downloading except a block that says so in as many words.
===== FILE (required): src/components/ShipmentQuoteForm.astro =====
---
/**
* ShipmentQuoteForm
*
* A freight quote request in one rounded, hairline-bordered card: an optional
* portrait photo on the left and a real form on the right, in two fieldsets —
* "Personal Info" (name, email, phone) and "Shipment Info" (freight type,
* city of departure, delivery city, incoterms, weight, height, width,
* length) — separated by hairlines, then an orange "Request A Quote ↗" pill.
*
* Every field is a pill with a small orange icon or chevron. The design shows
* placeholders only, so every control also has a real, visually hidden
* <label>. `action` is unset by default: point it at your handler.
*
* Appear on scroll: the photo slides in from −40px, the two fieldsets and the
* button rise 40px, staggered 0.1s. Without JS or under reduced motion
* everything is at rest.
*
* Reads --tg-* design tokens from any ancestor, with fallbacks.
*/
export interface ShipmentQuoteLabels {
name?: string;
email?: string;
phone?: string;
freightType?: string;
origin?: string;
destination?: string;
incoterm?: string;
weight?: string;
height?: string;
width?: string;
length?: string;
}
export interface ShipmentQuoteFormProps {
/** Where the form posts. Unset: the browser submits to the current URL. */
action?: string;
method?: "get" | "post";
formName?: string;
personalLegend?: string;
shipmentLegend?: string;
submitLabel?: string;
/** Freight type options (required select). `[]` drops the select. */
freightTypes?: string[];
/** Incoterm options (optional select). `[]` drops the select. */
incoterms?: string[];
/** Accessible labels (visually hidden). */
labels?: ShipmentQuoteLabels;
/** Placeholders — hints only, never the label. */
placeholders?: ShipmentQuoteLabels;
/** The portrait photo. Unset or `null`: none, and the form spans the card. `src: ""` renders the labelled placeholder. */
image?: { src?: string; alt?: string } | null;
/** The band around the card. */
background?: string;
fontStylesheet?: string;
class?: string;
/** Root id. Also seeds every field id. */
id?: string;
}
interface Props extends ShipmentQuoteFormProps {}
const {
action,
method = "post",
formName,
personalLegend = "Personal Info",
shipmentLegend = "Shipment Info",
submitLabel = "Request A Quote",
freightTypes = ["Air Freight", "Sea Freight", "Road Freight", "Rail Freight", "Warehousing"],
incoterms = ["EXW", "FCA", "FOB", "CFR", "CIF", "DAP", "DDP"],
labels = {},
placeholders = {},
image: imageProp,
background = "#ffffff",
fontStylesheet = "https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&family=DM+Sans:wght@400;500;600&display=swap",
class: className,
id,
} = Astro.props;
/* No photo unless one is passed; a passed photo is taken as given, alt "" (decorative) if unset. */
const image = imageProp ? { src: imageProp.src ?? "", alt: imageProp.alt ?? "" } : null;
const uid = (id ?? `sqf-${Math.random().toString(36).slice(2, 9)}`).replace(/[^A-Za-z0-9_-]/g, "") || "sqf";
const fid = (name: string) => `${uid}-${name}`;
const L: Required<ShipmentQuoteLabels> = {
name: "Your name",
email: "Your email",
phone: "Phone number",
freightType: "Freight type",
origin: "City of departure",
destination: "Delivery city",
incoterm: "Incoterms",
weight: "Weight (kg)",
height: "Height (cm)",
width: "Width (cm)",
length: "Length (cm)",
...labels,
};
const P: Required<ShipmentQuoteLabels> = {
name: "Your Name",
email: "Your Email",
phone: "Phone No:",
freightType: "Freight Type",
origin: "City Departure",
destination: "Delivery City",
incoterm: "Incoterms",
weight: "Weight",
height: "Height",
width: "Width",
length: "Length",
...placeholders,
};
const dims = [
{ key: "weight", name: "weight" },
{ key: "height", name: "height" },
{ key: "width", name: "width" },
{ key: "length", name: "length" },
] as const;
---
{fontStylesheet && <link rel="stylesheet" href={fontStylesheet} />}
<section class:list={["sqf", className]} id={id} data-sqf-root style={`--sqf-band: ${background};`}>
<div class="sqf-inner">
<div class:list={["sqf-card", { "sqf-card--solo": !image }]}>
{
image && (
<div class="sqf-media" data-sqf-appear="left">
{image.src ? (
<img src={image.src} alt={image.alt} width="716" height="964" loading="lazy" decoding="async" />
) : (
<div class="sqf-ph" role="img" aria-label="Image placeholder: truck in a freight yard, portrait">
<div class="sqf-ph-text" aria-hidden="true">
<span class="sqf-ph-label">Image</span>
<span class="sqf-ph-hint">Truck in a freight yard, portrait</span>
<span class="sqf-ph-spec">~3:4 · 716 × 964 px</span>
</div>
</div>
)}
</div>
)
}
<form class="sqf-form" action={action} method={method} name={formName}>
<fieldset class="sqf-set" data-sqf-appear style="--sqf-i: 0">
<legend class="sqf-legend">{personalLegend}</legend>
<div class="sqf-row sqf-row--3">
<div class="sqf-field">
<label class="sqf-sr" for={fid("name")}>{L.name}</label>
<input class="sqf-input" type="text" id={fid("name")} name="name" autocomplete="name" placeholder={P.name} required />
<svg class="sqf-icon" viewBox="0 0 12 12" aria-hidden="true"><path fill="currentColor" d="M6 6a2.6 2.6 0 1 0 0-5.2A2.6 2.6 0 0 0 6 6Zm0 1.1c-2.4 0-4.6 1.2-4.6 2.9v1.2h9.2V10c0-1.7-2.2-2.9-4.6-2.9Z" /></svg>
</div>
<div class="sqf-field">
<label class="sqf-sr" for={fid("email")}>{L.email}</label>
<input class="sqf-input" type="email" id={fid("email")} name="email" autocomplete="email" placeholder={P.email} required />
<svg class="sqf-icon" viewBox="0 0 12 12" aria-hidden="true"><path fill="currentColor" d="M1 2.5h10a.5.5 0 0 1 .5.5v.4L6 6.6.5 3.4V3a.5.5 0 0 1 .5-.5Zm-.5 2.1L6 7.8l5.5-3.2V9a.5.5 0 0 1-.5.5H1a.5.5 0 0 1-.5-.5Z" /></svg>
</div>
<div class="sqf-field">
<label class="sqf-sr" for={fid("phone")}>{L.phone}</label>
<input class="sqf-input" type="tel" id={fid("phone")} name="phone" autocomplete="tel" placeholder={P.phone} />
<svg class="sqf-icon" viewBox="0 0 12 12" aria-hidden="true"><path fill="currentColor" d="M3.3 5.3a8 8 0 0 0 3.4 3.4l1.1-1.1a.5.5 0 0 1 .5-.13 5.7 5.7 0 0 0 1.8.29.5.5 0 0 1 .5.5V10a.5.5 0 0 1-.5.5A8.5 8.5 0 0 1 1.5 2a.5.5 0 0 1 .5-.5h1.75a.5.5 0 0 1 .5.5c0 .63.1 1.23.29 1.8a.5.5 0 0 1-.13.5Z" /></svg>
</div>
</div>
</fieldset>
<fieldset class="sqf-set" data-sqf-appear style="--sqf-i: 1">
<legend class="sqf-legend">{shipmentLegend}</legend>
<div class="sqf-row sqf-row--3">
{freightTypes.length > 0 && (
<div class="sqf-field sqf-field--select">
<label class="sqf-sr" for={fid("freight")}>{L.freightType}</label>
<select class="sqf-input sqf-select" id={fid("freight")} name="freightType" required>
<option value="" selected disabled>{P.freightType}</option>
{freightTypes.map((o) => <option value={o}>{o}</option>)}
</select>
<svg class="sqf-chevron" viewBox="0 0 16 16" aria-hidden="true"><path d="m3 6 5 5 5-5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" /></svg>
</div>
)}
<div class="sqf-field">
<label class="sqf-sr" for={fid("origin")}>{L.origin}</label>
<input class="sqf-input sqf-input--plain" type="text" id={fid("origin")} name="originCity" autocomplete="address-level2" placeholder={P.origin} required />
</div>
<div class="sqf-field">
<label class="sqf-sr" for={fid("destination")}>{L.destination}</label>
<input class="sqf-input sqf-input--plain" type="text" id={fid("destination")} name="destinationCity" placeholder={P.destination} required />
</div>
</div>
<div class="sqf-row sqf-row--split">
{incoterms.length > 0 && (
<div class="sqf-field sqf-field--select">
<label class="sqf-sr" for={fid("incoterm")}>{L.incoterm}</label>
<select class="sqf-input sqf-select" id={fid("incoterm")} name="incoterm">
<option value="" selected>{P.incoterm}</option>
{incoterms.map((o) => <option value={o}>{o}</option>)}
</select>
<svg class="sqf-chevron" viewBox="0 0 16 16" aria-hidden="true"><path d="m3 6 5 5 5-5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" /></svg>
</div>
)}
<div class="sqf-row sqf-row--dims">
{dims.map((d) => (
<div class="sqf-field">
<label class="sqf-sr" for={fid(d.name)}>{L[d.key]}</label>
<input class="sqf-input sqf-input--plain sqf-input--dim" type="text" inputmode="decimal" id={fid(d.name)} name={d.name} placeholder={P[d.key]} />
</div>
))}
</div>
</div>
</fieldset>
<div class="sqf-submit-wrap" data-sqf-appear style="--sqf-i: 2">
<button class="sqf-btn" type="submit">
{submitLabel}
<svg class="sqf-btn-arrow" viewBox="0 0 12 12" width="11" height="11" aria-hidden="true">
<path d="M2 10 10 2M3.5 2H10v6.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</button>
</div>
</form>
</div>
</div>
</section>
<style>
.sqf {
--sqf-navy: var(--tg-navy, #020e28);
--sqf-orange: var(--tg-orange, #fd5523);
--sqf-muted: var(--tg-muted, #61697d);
--sqf-line: #d8dde1;
--sqf-display: var(--tg-font-display, Outfit, "Outfit Placeholder", ui-sans-serif, system-ui, sans-serif);
--sqf-body: var(--tg-font-body, "DM Sans", "DM Sans Placeholder", ui-sans-serif, system-ui, sans-serif);
--sqf-ease: cubic-bezier(0.22, 1, 0.36, 1);
box-sizing: border-box;
padding: 60px 0;
/* Contains the photo's −40px appear slide on narrow screens. */
overflow-x: clip;
background: var(--sqf-band);
color: var(--sqf-navy);
font-family: var(--sqf-body);
}
.sqf *,
.sqf *::before,
.sqf *::after {
box-sizing: border-box;
}
.sqf-inner {
max-width: 1293px;
margin: 0 auto;
padding: 0 20px;
}
.sqf-card {
display: flex;
flex-direction: column;
gap: 30px;
padding: 16px;
border: 1px solid var(--sqf-line);
border-radius: 30px;
}
/* On phones and tablets the form comes first and the photo follows it,
as the reference does. The photo holds nothing focusable, so the swap
cannot scramble the tab order. */
.sqf-media {
order: 1;
position: relative;
aspect-ratio: 4 / 3.5;
border-radius: 20px;
overflow: hidden;
}
.sqf-media img {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.sqf-form {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 0;
margin: 0;
}
.sqf .sqf-set {
min-width: 0;
margin: 0;
padding: 0 0 16px;
border: 0;
border-bottom: 1px solid var(--sqf-line);
}
.sqf .sqf-legend {
float: left;
width: 100%;
margin: 0 0 16px;
padding: 0;
font: 500 20px/34px var(--sqf-display);
color: var(--sqf-navy);
}
/* The floated legend needs its following rows cleared. */
.sqf-legend + .sqf-row {
clear: both;
}
.sqf-row {
display: grid;
gap: 12px;
min-width: 0;
}
.sqf-row + .sqf-row {
margin-top: 12px;
}
.sqf-row--dims {
margin-top: 0;
}
.sqf-field {
position: relative;
min-width: 0;
}
.sqf-input {
display: block;
width: 100%;
min-height: 44px;
margin: 0;
padding: 11px 44px 11px 18px;
border: 1px solid var(--sqf-line);
border-radius: 100px;
background: #fff;
color: var(--sqf-navy);
font: 400 16px/20px var(--sqf-body);
transition: border-color 0.3s ease-out;
}
.sqf-input--plain {
padding-right: 18px;
}
.sqf-input::placeholder {
color: var(--sqf-muted);
opacity: 1;
}
.sqf-input:hover {
border-color: #b9c0c7;
}
/* An outline, not a shadow: it survives forced colours. */
.sqf-input:focus-visible,
.sqf-input:focus {
outline: 2px solid var(--sqf-orange);
outline-offset: 2px;
}
.sqf-select {
appearance: none;
-webkit-appearance: none;
cursor: pointer;
}
.sqf-select:has(option[value=""]:checked) {
color: var(--sqf-muted);
}
.sqf-select option {
color: var(--sqf-navy);
}
.sqf-icon,
.sqf-chevron {
position: absolute;
top: 50%;
right: 20px;
width: 12px;
height: 12px;
transform: translateY(-50%);
color: var(--sqf-orange);
pointer-events: none;
}
.sqf-chevron {
width: 18px;
height: 18px;
right: 18px;
color: var(--sqf-muted);
}
.sqf-submit-wrap {
padding-top: 8px;
}
.sqf-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
width: 100%;
min-height: 50px;
padding: 18px 20px;
border: 0;
border-radius: 100px;
background: var(--sqf-orange);
color: #fff;
font: 600 14px/1 var(--sqf-body);
cursor: pointer;
transition: background-color 0.35s ease-out;
}
.sqf-btn-arrow {
flex: none;
transition: transform 0.35s ease-out;
}
.sqf-btn:hover {
background: var(--sqf-navy);
}
.sqf-btn:hover .sqf-btn-arrow {
transform: rotate(45deg);
}
.sqf-btn:focus-visible {
outline: 2px solid var(--sqf-navy);
outline-offset: 3px;
}
.sqf-sr {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
.sqf-ph {
position: absolute;
inset: 0;
display: grid;
place-items: center;
padding: 16px;
text-align: center;
background: repeating-linear-gradient(135deg, #e6e9ee 0 12px, #edf0f3 12px 24px);
color: rgb(2 14 40 / 0.62);
}
.sqf-ph-text {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.sqf-ph-label {
font: 500 12px/1 var(--sqf-body);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.sqf-ph-hint {
max-width: 28ch;
font: 500 14px/1.4 var(--sqf-body);
}
.sqf-ph-spec {
font: 400 12px/1.2 var(--sqf-body);
}
/* ---- appear ---- */
[data-sqf-appear] {
transition:
opacity 0.8s var(--sqf-ease),
transform 0.8s var(--sqf-ease);
transition-delay: calc(var(--sqf-i, 0) * 0.1s);
}
.sqf[data-sqf-anim] [data-sqf-appear]:not([data-in]) {
opacity: 0;
transform: translateY(40px);
}
.sqf[data-sqf-anim] [data-sqf-appear="left"]:not([data-in]) {
transform: translateX(-40px);
}
/* ---- tablet: three-up rows, photo still below ---- */
@media (min-width: 810px) {
.sqf {
padding: 90px 0;
}
.sqf-card {
padding: 30px;
}
.sqf-form {
gap: 25px;
}
.sqf-submit-wrap {
padding-top: 11px;
}
.sqf .sqf-set {
padding-bottom: 25px;
}
.sqf .sqf-legend {
margin-bottom: 25px;
font-size: 24px;
}
.sqf-row {
gap: 25px 30px;
}
.sqf-row + .sqf-row {
margin-top: 25px;
}
.sqf-row--dims {
margin-top: 0;
}
.sqf-row--3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.sqf-row--split {
grid-template-columns: minmax(0, 228fr) minmax(0, 482fr);
}
.sqf-row--dims {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
/* Incoterms dropped: the four measurements take the whole row. */
.sqf-row--split:has(> .sqf-row--dims:only-child) {
grid-template-columns: minmax(0, 1fr);
}
.sqf-input {
min-height: 56px;
padding: 17px 44px 17px 24px;
}
.sqf-input--plain {
padding-right: 24px;
}
.sqf-input--dim {
padding-inline: 10px;
text-align: center;
}
.sqf-icon {
right: 24px;
}
.sqf-btn {
width: 240px;
min-height: 58px;
padding: 22px 20px;
}
.sqf-media {
aspect-ratio: 16 / 9;
}
}
/* ---- desktop: photo left, form right ---- */
@media (min-width: 1200px) {
.sqf {
padding: 120px 0;
}
.sqf-card {
display: grid;
grid-template-columns: minmax(0, 358fr) minmax(0, 740fr);
justify-content: space-between;
align-items: start;
gap: 95px;
}
.sqf-card--solo {
grid-template-columns: minmax(0, 1fr);
}
.sqf-media {
order: 0;
align-self: stretch;
aspect-ratio: auto;
min-height: 482px;
}
}
@media (prefers-reduced-motion: reduce) {
.sqf-btn:hover .sqf-btn-arrow {
transform: none;
}
[data-sqf-appear] {
transition: none;
}
}
@media (forced-colors: active) {
.sqf-btn {
border: 1px solid ButtonText;
}
.sqf-icon,
.sqf-chevron {
color: CanvasText;
}
}
</style>
<script>
/*
* Appear on scroll only. The form itself is native and needs no script.
* One IntersectionObserver per instance; each watched element gets
* `data-in` once. Released on astro:before-swap.
*/
function initShipmentQuoteForm(root: HTMLElement) {
if (root.dataset.sqfReady !== undefined) return;
root.dataset.sqfReady = "";
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
if (!("IntersectionObserver" in window)) return;
const controller = new AbortController();
const { signal } = controller;
root.dataset.sqfAnim = "";
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
(entry.target as HTMLElement).dataset.in = "";
observer.unobserve(entry.target);
}
},
{ threshold: 0.1 },
);
root.querySelectorAll<HTMLElement>("[data-sqf-appear]").forEach((el) => observer.observe(el));
signal.addEventListener("abort", () => observer.disconnect());
document.addEventListener(
"astro:before-swap",
() => {
controller.abort();
delete root.dataset.sqfReady;
delete root.dataset.sqfAnim;
},
{ once: true, signal },
);
}
const boot = () =>
document.querySelectorAll<HTMLElement>("[data-sqf-root]").forEach(initShipmentQuoteForm);
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
document.addEventListener("astro:page-load", boot);
</script>
===== END src/components/ShipmentQuoteForm.astro =====
===== FILE (supporting): src/components/ShipmentQuoteForm.md =====
# Shipment quote form
A freight quote request in one rounded, hairline-bordered card: an optional
portrait photo on the left, a real form on the right in two fieldsets — **Personal
Info** (name, email, phone) and **Shipment Info** (freight type, city of
departure, delivery city, incoterms, weight, height, width, length) —
separated by hairlines, then an orange **Request A Quote ↗** pill.
Catalog page: `/components/shipment-quote-form`
## Purpose
The body of a "Request a quote" page. Collects enough to price a lane in one
screen. Part of the Northpass (Transigo-style) family; reads the `--tg-*`
tokens.
## Files required
**`ShipmentQuoteForm.astro`** — the whole component. No imports.
`ShipmentQuoteForm.md` — this file.
`public/shipment-quote-form/truck-yard.webp` (716×964) — the catalog demo's
photo, passed as a prop. Only needed if you pass it: there is no default
photo, so with no props the form fills the whole card.
## Basic usage
```astro
<ShipmentQuoteForm action="/api/quote" method="post" />
```
## It has no back end
**`action` is unset by default**, so the browser submits to the current URL.
Point it at your handler. There is no success/error state; whatever receives
the request owns the response.
Field names as submitted: `name`, `email`, `phone`, `freightType`,
`originCity`, `destinationCity`, `incoterm`, `weight`, `height`, `width`,
`length`. Required: name, email, freight type, both cities.
## Props
| Prop | Type | Default | What it does |
| :--- | :--- | :--- | :--- |
| `action` | `string?` | — | See above. |
| `method` | `"get" \| "post"?` | `"post"` | |
| `formName` | `string?` | — | Added to the `<form>`. |
| `personalLegend` | `string?` | `"Personal Info"` | First fieldset's legend. |
| `shipmentLegend` | `string?` | `"Shipment Info"` | Second fieldset's legend. |
| `submitLabel` | `string?` | `"Request A Quote"` | |
| `freightTypes` | `string[]?` | Air, Sea, Road, Rail, Warehousing | Required select. `[]` drops it. |
| `incoterms` | `string[]?` | EXW, FCA, FOB, CFR, CIF, DAP, DDP | Optional select. `[]` drops it. |
| `labels` | `ShipmentQuoteLabels?` | English | The visually hidden labels (e.g. `"Weight (kg)"`). |
| `placeholders` | `ShipmentQuoteLabels?` | as the design | Hints only. |
| `image` | `{ src?, alt? } \| null` | none | Unset or `null`: no photo column. `src: ""` → labelled placeholder. `alt` defaults to `""` (decorative). |
| `background` | `string?` | `#ffffff` | The band around the card. |
| `fontStylesheet` | `string?` | Outfit + DM Sans | `""` skips the link. |
| `class` | `string?` | — | |
| `id` | `string?` | — | Root id. **Also seeds every field id**; random when omitted, so two forms never collide. |
`ShipmentQuoteLabels` keys: `name, email, phone, freightType, origin,
destination, incoterm, weight, height, width, length`.
## Behaviour
| Element | Effect | Measured / chosen |
| :--- | :--- | :--- |
| Submit hover | background `#fd5523` → `#020e28`, ↗ rotates 45° | as measured; 0.35s ease-out |
| Field hover | hairline darkens `#d8dde1` → `#b9c0c7` | addition |
| Field focus | 2px orange outline, 2px offset | see Accessibility |
| Photo appear | from `translateX(-40px)`, fading | split-section rule from the brief |
| Fieldsets and button appear | from `translateY(40px)`, staggered 0.1s | form-row rule from the brief |
One IntersectionObserver per instance; hidden poses only while the root has
`data-sqf-anim`; released on `astro:before-swap`.
## Accessibility
- Two real `<fieldset>`s with `<legend>`s, which is what groups the fields for
assistive technology (the reference uses `h5`s).
- **Every control has a real `<label for>`**, visually hidden because the
design shows placeholders only. No placeholder is doing a label's job.
- Inputs are **16px** so iOS Safari does not zoom on focus.
- `autocomplete` on name, email, phone and origin city; dimensions use
`inputmode="decimal"`.
- The focus indicator is an `outline`, so it survives forced colours; icons
and chevrons are `aria-hidden` and `pointer-events: none`.
- The select placeholder is a disabled, pre-selected empty option on the
required select (so `required` works) and a normal empty option on the
optional one; both render in the muted colour while empty.
## Reduced motion / without JavaScript
Nothing starts hidden; the arrow does not rotate. The form is entirely native
and works identically without JS.
## Responsive
| Width | Layout |
| :--- | :--- |
| ≥ 1200 | photo 358 / form 740 (of 1193), 95px apart, card padding 30, radius 30; 56px pills; rows of three; incoterms 228 + four 98px dimension pills |
| 810–1199 | form full width with the same rows; photo below at 16:9 |
| < 810 | card padding 16; every field full width, 44px min height; full-width button; photo below the form (as the phone capture) |
## Images and credits
`truck-yard.webp` (catalog demo only) — Unsplash `9unRFufhNUw` (Unsplash
License — free for commercial use, no attribution required), optimised to
WebP. Decorative (`alt=""`).
## Approximations
- **Appear.** The quote page's own appear offsets were not sampled; the
family's measured rules (split image from −40px, form rows rise 40px) are
applied.
- **Card border and field hairlines** use `#d8dde1`, the measured line colour;
the dump omits the card border, which is read from the screenshot.
- **Field focus** is an outline, not the reference's shadow transition, so it
survives forced colours.
- **Chevrons** are muted grey here as on the reference quote page (the
contact page's are orange).
- Tablet layout is inferred.
===== END src/components/ShipmentQuoteForm.md =====
===== FILE (required): public/shipment-quote-form/truck-yard.webp =====
[binary asset — download https://astro.baysixmedia.com/shipment-quote-form/truck-yard.webp and save it to public/shipment-quote-form/truck-yard.webp]
--- VERIFIED IN THE COMPONENT LIBRARY ---
Enrolled for verification but never run.
No verification run has been recorded.
No checks have passed yet.
No usable source fingerprint, so these results cannot be tied to the files below.
--- NOT PROVEN ---
- ClientRouter: event-tested only. Handlers respond to dispatched lifecycle events; this has NOT been certified against a real <ClientRouter /> transition.
--- WHAT THIS DOES NOT CERTIFY ---
Verification was performed on the unmodified component, in an isolated fixture, in the
library. It says nothing about this project's CSS, routes, layout or data, and nothing
about the component once you adapt it. A verified component is a good starting point,
not a guarantee about the installation you are about to build.
--- TEST IN THIS PROJECT AFTER INSTALLING OR CUSTOMISING ---
- Horizontal overflow at 1440, 1280, 1024, 834, 390 and 320px.
- Keyboard operation end to end, including visible focus.
- The no-JavaScript render.
- prefers-reduced-motion: reduce.
- Two instances on one page, if this project renders more than one.
- If this project uses <ClientRouter />: teardown and reinitialisation across a real navigation.