/* ==========================================================================
   Crafted in 3D: matches craftedin3d.com's identity, tightened.
   Black + #FFC700 yellow + white. Konkhmer Sleokchher display, Noto Sans body.
   Single committed brand look (not theme-reactive).
   ========================================================================== */

/* Self-hosted so the site has no third-party font dependency (privacy, speed,
   works offline / behind the tunnel). Latin subsets only; woff2 pulled from
   Google Fonts (Konkhmer Sleokchher v3 OFL, Noto Sans v42 OFL). */
@font-face {
  font-family: "Konkhmer Sleokchher";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/konkhmer-sleokchher-latin-400.woff2") format("woff2");
}
@font-face {
  font-family: "Noto Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/noto-sans-latin-var.woff2") format("woff2");
}

:root {
  --yellow: #ffc700;
  --yellow-hi: #ffd23f;
  --black: #000000;
  --near-black: #111111;
  --white: #ffffff;
  --ink: #141414;
  --ink-soft: #4a4a4a;
  --heading: #383838;          /* headings on light backgrounds (matches craftedin3d.com) */
  --line: #e2e2e2;
  --line-dark: #2a2a2a;
  --panel: #ffffff;
  --bg: #fafaf7;
  --muted: #6b6b64;
  --hover: #fff7dc;
  --charcoal2: #1a1a1c;
  --maxw: 1140px;
  --pad: clamp(1.1rem, 4vw, 2.75rem);
  /* Soft corners and the box shadow (owner 2026-10-24), measured on Xometry's quote box: 8 px corners on the box and on its
     button, and the box's three-layer shadow. Used by the "Soft corners and shadows" section at the end of this file. */
  --radius: 8px;
  --radius-sm: 4px;
  --shadow-box: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

/* Konkhmer Sleokchher mangles ? and !, so just those glyphs go to a clean sans.
   Placed first in the heading font stack below. 2026-10-20 (voice sample): the bold cut of each sans comes first, so the
   question mark is as heavy as the heading letters beside it (the regular cut read as a thin, lighter "?"). */
@font-face {
  font-family: "HeadingPunct";
  src: local("Arial Bold"), local("Arial-BoldMT"), local("Segoe UI Bold"), local("SegoeUI-Bold"),
       local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), local("Roboto Bold"), local("Roboto-Bold"),
       local("Arial"), local("Segoe UI"), local("Helvetica Neue"), local("Roboto");
  unicode-range: U+003F, U+0021;
}

body {
  margin: 0;
  background: var(--white);
  color: var(--ink);
  font-family: "Noto Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 18px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: "HeadingPunct", "Konkhmer Sleokchher", system-ui, sans-serif;
  font-weight: 400;
  line-height: 1.15;
  color: var(--heading);
  margin: 0 0 0.5em;
  text-wrap: balance;
}
/* Yellow headings only on dark grounds */
.hero h1, .hero h2, .hero h3,
.section.on-black h1, .section.on-black h2, .section.on-black h3,
.page-head h1, .page-head p + h1,
.quote-card.alt h2 { color: var(--yellow); }
.process-item h3 { color: var(--black); }
h1 { font-size: clamp(2.4rem, 6vw, 4rem); }
h2 { font-size: clamp(1.9rem, 4.5vw, 2.9rem); }
h3 { font-size: clamp(1.3rem, 2.6vw, 1.6rem); }

p { margin: 0 0 1.1rem; }
a { color: var(--ink); }
img { max-width: 100%; height: auto; display: block; }

.wrap {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--yellow);
  color: var(--black);
  padding: 0.6rem 1rem;
  font-weight: 700;
  z-index: 100;
}
.skip-link:focus { left: 0; }

:focus-visible { outline: 3px solid var(--yellow); outline-offset: 2px; }

/* -------------------------------------------------------------------------- */
/* Buttons: one primary, one secondary. Corners: see "Soft corners" at the end. */
/* -------------------------------------------------------------------------- */
.btn {
  display: inline-block;
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-weight: 400;
  font-size: 1.05rem;
  line-height: 1;
  text-decoration: none;
  padding: 1rem 2rem;
  border: 2px solid var(--yellow);
  cursor: pointer;
  transition: background-color 0.14s ease, color 0.14s ease;
}
.btn-primary { background: var(--yellow); color: var(--black); }
.btn-primary:hover { background: var(--yellow-hi); border-color: var(--yellow-hi); }
/* Without this, a disabled primary button (the cart's Pay/Send button
   while it waits on a fulfilment choice, review finding S1) is pixel-
   identical to an enabled one -- same solid yellow, same contrast -- so a
   customer can click it, get nothing, and read the page as broken. */
.btn-primary:disabled, .btn-primary[disabled], .btn-primary[aria-disabled="true"] {
  background: var(--line);
  border-color: var(--line);
  color: var(--muted);
  cursor: not-allowed;
}
.btn-primary:disabled:hover, .btn-primary[disabled]:hover, .btn-primary[aria-disabled="true"]:hover { background: var(--line); border-color: var(--line); }
.btn-secondary { background: transparent; color: var(--yellow); }
.btn-secondary:hover { background: var(--yellow); color: var(--black); }
/* A third, quiet variant: yellow is reserved for the one primary action on
   a panel (Continue to order), so a same-panel secondary/destructive-ish
   action like Clear order gets a plain muted outline instead. */
.btn-quiet { font: inherit; font-size: 0.95rem; padding: 0.7rem 1.2rem; background: transparent;
  color: var(--muted); border: 2px solid var(--line); cursor: pointer; transition: border-color 150ms ease, color 150ms ease; }
.btn-quiet:hover { border-color: var(--ink-soft); color: var(--ink); }

/* Inline text link styled like the site's "View our services" underline */
.textlink {
  /* Inline links keep the body face (the heading face mid-sentence read as a highlight, owner 2026-10-19). */
  font-family: inherit;
  font-weight: 600;
  color: var(--heading);
  text-decoration: none;
  border-bottom: 2px solid var(--yellow);
  padding-bottom: 1px;
}
.textlink:hover { border-bottom-color: var(--heading); }
.on-black .textlink { color: var(--yellow); border-bottom-color: var(--yellow); }

/* -------------------------------------------------------------------------- */
/* Header                                                                    */
/* -------------------------------------------------------------------------- */
.site-header {
  background: var(--black);
  position: sticky;
  top: 0;
  z-index: 50;
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 96px;
  gap: 1rem;
}
.brand {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.brand img {
  height: 72px;
  width: auto;
  display: block;
}
@media (max-width: 999px) { .brand img { height: 54px; } }
.site-nav ul {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 1.75rem;
  margin: 0;
  padding: 0;
}
.site-nav a {
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 1rem;
  color: var(--yellow);
  text-decoration: none;
  padding: 0.4rem 0;
  border-bottom: 2px solid transparent;
}
.site-nav a:hover { border-bottom-color: var(--yellow); }
.site-nav a[aria-current="page"] { border-bottom-color: var(--yellow); }

/* "Get instant quote": pulled out of the link row as a solid-yellow CTA */
.site-nav li.nav-cta { margin-left: 0.5rem; }
.site-nav li.nav-cta a {
  color: var(--heading);
  background: var(--yellow);
  border: 2px solid var(--yellow);
  border-bottom-width: 2px;
  padding: 0.5rem 1rem;
  line-height: 1;
  white-space: nowrap;
}
.site-nav li.nav-cta a:hover { background: var(--yellow-hi); border-color: var(--yellow-hi); }
.site-nav li.nav-cta a[aria-current="page"] { background: var(--yellow-hi); border-color: var(--yellow-hi); }
/* The full menu is 1027px wide at its desktop size, so it only fits from about
   1211px up with a scrollbar. Between 1000px and 1239px it is set tighter, and below 1000px the
   menu button takes over (it was 900px: the page scrolled sideways between
   901px and about 1190px, board card 496, 2026-10-10). */
@media (min-width: 1000px) and (max-width: 1239px) {
  .site-nav ul { gap: 0.9rem; }
  .site-nav a { font-size: 0.86rem; }
  .site-nav li.nav-cta { margin-left: 0.25rem; }
  .site-nav li.nav-cta a { padding: 0.45rem 0.7rem; }
  .brand img { height: 60px; }
}
/* Builds with accounts open (staging today) carry one more item, the account link after the quote button. Set closer together it still fits beside the
   logo (with Industries back in the menu it ran 63 px past the header's edge at 1240 px and up, 2026-10-24); builds without it
   keep the spacing above. Signed in, My Account takes that place and is 88 px wider than Sign in (2026-10-25): closer still,
   and from 1000 to 1239 px it shows its words and chevron without the icon. */
@media (min-width: 1240px) {
  .site-nav--account ul { gap: 0.75rem; }
  .site-nav--account li.nav-cta { margin-left: 0; }
}
@media (min-width: 1000px) and (max-width: 1239px) {
  .site-nav--account ul { gap: 0.6rem; }
}
/* The account link (accounts open; owner 2026-10-25, after Xometry's "Log In / Register" and nearly every other site): plain text
   on its own at the far right, after the quote button, 1rem from it; on phones in the bar beside the menu button, never inside
   the menu. The menu keeps its place by taking the free space to its left. */
.hdr-acct { display: flex; align-items: center; flex-shrink: 0; }
.hdr-acct-link {
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 1rem;
  color: var(--yellow);
  text-decoration: none;
  padding: 0.4rem 0;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.hdr-acct-link:hover { border-bottom-color: var(--yellow); }
@media (min-width: 1000px) { .site-nav--account { margin-left: auto; } }
@media (min-width: 1000px) and (max-width: 1239px) { .hdr-acct-link { font-size: 0.86rem; } }
@media (max-width: 999px) {
  .hdr-acct { order: 2; margin-left: auto; }
  .nav-toggle-btn { order: 3; }
}
/* The My Account menu (owner 2026-10-25, Xometry's "My Account" menu, our look; partials/acct-menu.njk, opened by nav.js). Signed
   in, it stands where Sign in was: a person in a circle, the words, a chevron. The panel hangs under the button, right-aligned,
   in the nav dropdowns' look; on phones it opens across the screen under the bar, as the menu does. */
.hdr-menu { position: relative; flex-shrink: 0; }
.hdr-menu-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0;
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--yellow);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  white-space: nowrap;
}
.hdr-menu-btn:hover, .hdr-menu-btn[aria-expanded="true"] { border-bottom-color: var(--yellow); }
.hdr-menu-ico { flex: none; width: 20px; height: 20px; }
.hdr-menu-chev { flex: none; width: 10px; height: 10px; transition: transform 0.15s ease; }
.hdr-menu-btn[aria-expanded="true"] .hdr-menu-chev { transform: rotate(180deg); }
@media (min-width: 1000px) and (max-width: 1239px) {
  .site-nav--account ~ .hdr-acct .hdr-menu-btn { font-size: 0.86rem; }
  .site-nav--account ~ .hdr-acct .hdr-menu-ico { display: none; }
}
.hdr-menu-panel {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 60;
  min-width: 14rem;
  margin: 0;
  padding: 0.4rem 0;
  list-style: none;
  background: var(--black);
  border-top: 2px solid var(--yellow);
  box-shadow: 0 16px 28px rgba(0, 0, 0, 0.4);
}
.hdr-menu-panel form { margin: 0; }
.hdr-menu-panel a, .hdr-menu-panel button {
  display: block;
  width: 100%;
  padding: 0.55rem 1.1rem;
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--yellow);
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  background: none;
  border: 0;
  cursor: pointer;
}
.hdr-menu-panel a:hover, .hdr-menu-panel button:hover, .hdr-menu-panel a[aria-current="page"] { background: rgba(255, 255, 255, 0.08); color: var(--yellow-hi); }

/* The account header (account pages; partials/account-header.njk, Xometry's signed-in header): the logo, then at the right
   Orders, Quotes, Parts, My Account and the quote button, in place of the marketing menus. On phones the logo and My Account; the
   links and the button fold into its panel (the hdr-menu-phone items, hidden from 1000 px up). */
.acct-nav { display: flex; align-items: center; gap: 1.75rem; margin-left: auto; }
.acct-nav a {
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 1rem;
  color: var(--yellow);
  text-decoration: none;
  padding: 0.4rem 0;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.acct-nav a:hover, .acct-nav a[aria-current="page"] { border-bottom-color: var(--yellow); }
.site-header--account .hdr-menu { margin-left: 0.75rem; }
/* The size of the public header's quote button (.site-nav li.nav-cta a). */
.btn.acct-nav-cta { margin-left: 0.5rem; padding: 0.5rem 1rem; font-size: 1rem; line-height: 1; color: var(--heading); white-space: nowrap; }
@media (min-width: 1000px) { .hdr-menu-phone { display: none; } }
@media (max-width: 999px) {
  .acct-nav, .btn.acct-nav-cta { display: none; }
  .site-header--account .hdr-menu { margin-left: auto; }
  .hdr-menu { position: static; }
  .hdr-menu-panel {
    left: 0;
    min-width: 0;
    max-height: 80vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 0.5rem var(--pad) 1.5rem;
    border-top: 1px solid var(--line-dark);
  }
  .hdr-menu-panel a, .hdr-menu-panel button { padding: 0.8rem 0; font-size: 1.15rem; border-bottom: 2px solid transparent; }
  .hdr-menu-panel a:hover, .hdr-menu-panel button:hover { background: transparent; color: var(--yellow-hi); }
  .hdr-menu-panel a[aria-current="page"] { background: transparent; color: var(--yellow); border-bottom-color: var(--yellow); }
  .hdr-menu-cta { margin: 0.9rem 0; }
  .hdr-menu-panel .hdr-menu-cta a { padding: 0.85rem 1rem; line-height: 1; text-align: center; color: var(--heading); background: var(--yellow); border: 2px solid var(--yellow); }
  .hdr-menu-panel .hdr-menu-cta a:hover { color: var(--heading); background: var(--yellow-hi); border-color: var(--yellow-hi); }
}

/* Dropdown nav groups (board card 16: Services / Industries / Materials /
   Resources). The group label is a normal nav link (works with no JS/CSS
   support at all); the chevron beside it is a separate disclosure button
   that nav.js drives with aria-expanded, and that CSS also opens on
   :hover/:focus-within so a mouse or Tab alone opens it with no script. */
.site-nav .nav-group { position: relative; }
.nav-group-inner { display: flex; align-items: center; }
.nav-group.is-current > .nav-group-inner > .nav-group-link { border-bottom-color: var(--yellow); }
.nav-group-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-left: 0.1rem;
  padding: 0;
  background: none;
  border: 0;
  color: var(--yellow);
  cursor: pointer;
}
.nav-group-toggle::after {
  content: "";
  width: 0.4em;
  height: 0.4em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.nav-group.is-open .nav-group-toggle::after { transform: rotate(225deg); }

.site-nav .subnav {
  /* Overrides .site-nav ul's display:flex/gap (higher specificity here,
     but that rule never gets unset otherwise -- board card 16 QA: without
     this the dropdown rendered as a horizontal row and ran off-screen). */
  display: block;
  list-style: none;
  margin: 0;
  padding: 0.4rem 0;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 15rem;
  background: var(--black);
  border-top: 2px solid var(--yellow);
  box-shadow: 0 16px 28px rgba(0, 0, 0, 0.4);
  z-index: 60;
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0.14s;
}
.subnav li { width: 100%; }
.subnav a {
  display: block;
  padding: 0.55rem 1.1rem;
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 0.92rem;
  color: var(--yellow);
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 0;
}
.subnav a:hover,
.subnav a[aria-current="page"] {
  background: rgba(255, 255, 255, 0.08);
  color: var(--yellow-hi);
}

@media (min-width: 1000px) {
  .nav-group:hover .subnav,
  .nav-group:focus-within .subnav,
  .nav-group.is-open .subnav {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
  .nav-group:hover .nav-group-toggle::after,
  .nav-group:focus-within .nav-group-toggle::after {
    transform: rotate(225deg);
  }
  /* Escape (nav.js) returns focus to the group's own toggle, which sits
     inside this same .nav-group -- so :focus-within above would otherwise
     keep the panel open on screen even though JS just set
     aria-expanded="false". js-dismissed forces it shut until something
     opens it on purpose. :not(:hover) means a mouse user hovering the
     same group right after a keyboard Escape still opens it immediately,
     with no script involved. */
  .nav-group.js-dismissed:not(:hover):not(.is-open) .subnav {
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px);
  }
  .nav-group.js-dismissed:not(:hover):not(.is-open) .nav-group-toggle::after {
    transform: rotate(45deg);
  }
}

.nav-toggle-btn { display: none; }

@media (max-width: 999px) {
  .nav-toggle-btn {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    padding: 0;
    background: transparent;
    border: 0;
    cursor: pointer;
  }
  .nav-toggle-btn span { display: block; height: 2px; width: 26px; background: var(--yellow); transition: transform 0.2s, opacity 0.2s; }
  .site-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--black);
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.25s ease;
  }
  .site-nav ul { flex-direction: column; align-items: flex-start; gap: 0; padding: 0.5rem var(--pad) 1.5rem; }
  .site-nav li { width: 100%; }
  .site-nav a { display: block; padding: 0.8rem 0; font-size: 1.15rem; }
  .site-nav li.nav-cta { margin: 0.9rem 0 0; }
  .site-nav li.nav-cta a {
    text-align: center;
    padding: 0.85rem 1rem;
    font-size: 1.15rem;
    border-bottom-width: 2px;
  }
  /* S24 item 2 (2026-10-13): an open group can make the menu taller than 80vh (Resources open put Get a Quote under the
     fold at 375 by 812), so the open menu scrolls inside itself instead of clipping its last items. */
  .nav-toggle-btn[aria-expanded="true"] ~ .site-nav { max-height: 80vh; overflow-y: auto; overscroll-behavior: contain; }
  .nav-toggle-btn[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav-toggle-btn[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
  .nav-toggle-btn[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  /* Dropdown groups become collapsible accordion sections on mobile, same
     groups as desktop, each opened by its own chevron button. */
  .nav-group-inner { width: 100%; justify-content: space-between; }
  .nav-group-link { flex: 1 1 auto; }
  .nav-group-toggle { width: 2.4rem; height: 2.4rem; }
  .site-nav .subnav {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    box-shadow: none;
    background: transparent;
    border-top: 1px solid var(--line-dark);
    max-height: 0;
    overflow: hidden;
    padding: 0;
    transition: max-height 0.2s ease;
  }
  .nav-group.is-open .subnav { max-height: 40rem; padding: 0.2rem 0 0.3rem; }
  .subnav a { padding: 0.65rem 0 0.65rem 1rem; font-size: 1.05rem; color: #d8d8d8; }
  .subnav a:hover, .subnav a[aria-current="page"] { background: transparent; color: var(--yellow); }
}

/* -------------------------------------------------------------------------- */
/* Sections                                                                  */
/* -------------------------------------------------------------------------- */
.section { padding-block: clamp(3rem, 7vw, 5.5rem); }
.section.on-black { background: var(--black); }
.section.on-black p { color: #d8d8d8; }
.section--center { text-align: center; }
.section--center p { margin-inline: auto; max-width: 60ch; }

.hero {
  background: var(--black);
  text-align: center;
  padding-block: clamp(4rem, 12vw, 8rem);
}
.hero h1 { margin-bottom: 1.5rem; }
.hero .hero-sub {
  color: #d8d8d8;
  font-size: 1.15rem;
  max-width: 55ch;
  margin: 0 auto 2rem;
}
/* S23 (2026-10-13): a link in the hero's sentence reads on the black ground, as a page head's link does. */
.hero .hero-sub a { color: #fff; text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; }
.hero .hero-sub a:hover { color: var(--yellow); }
.hero .btn { margin-top: 0.5rem; }
/* The hero's two buttons, one under the other, both as wide as the wider one (as solid gold blocks, two widths read as a slip). */
.hero-actions { display: grid; justify-content: center; gap: 0.75rem; }
.hero-actions .btn { margin: 0; text-align: center; }
/* Owner, 2026-10-25: the hero's two buttons the other way round, solid gold, then black with the gold outline on hover. */
.hero-actions .btn { background: var(--yellow); color: var(--black); border-color: var(--yellow); }
.hero-actions .btn:hover, .hero-actions .btn:focus-visible { background: var(--black); color: var(--yellow); border-color: var(--yellow); }
/* The consultation button lands on the home page's contact form: leave room for the sticky header. */
#contactus { scroll-margin-top: 110px; }

/* Intro band (white): "Bring your ideas to life" */
.intro { text-align: center; padding-block: clamp(3rem, 7vw, 5rem); }
.intro p { max-width: 62ch; margin-inline: auto; font-size: 1.12rem; }

/* The process items (home page only). Voice sample 2026-10-20: two cards, side by side and centred, each as wide as one of the
   three was. */
.process {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  max-width: 50rem;
  margin-inline: auto;
  text-align: center;
}
@media (max-width: 820px) { .process { grid-template-columns: 1fr; max-width: 30rem; margin-inline: auto; } }
.process-item .icon {
  width: 104px;
  height: 104px;
  object-fit: contain;
  margin: 0 auto 1.25rem;
}
.process-item h3 { margin-bottom: 0.4rem; }
.process-item p { color: var(--ink-soft); font-size: 1rem; }
.process-cta { text-align: center; margin-top: 2.5rem; }

/* Gallery */
.gallery {
  /* Sample 2026-10-19 (owner: photos were cropped to 4:3, about half of each hidden). Column layout: every photo
     shows whole at its own shape; tiles stack in columns like a pinboard. */
  columns: 4 240px;
  column-gap: 1rem;
  margin-top: 2rem;
}
.gallery figure {
  margin: 0 0 1rem;
  break-inside: avoid;
  background: var(--panel);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
}
.gallery img {
  width: 100%;
  height: auto;
  display: block;
  background: #f3f3f3;
}
/* One part per row (3D scanning page, owner 2026-10-22): the photos and CAD renders of a part sit side by side, whole. */
.gallery.gallery--group { display: grid; columns: auto; grid-template-columns: repeat(var(--n, 3), 1fr); gap: 1rem; align-items: start; margin-top: 1.25rem; }
.gallery.gallery--group figure { margin: 0; }
/* Four to a part (the gate latch): two pairs, each photo beside its render, so they are not left thumbnail size. */
.gallery.gallery--group[style*="--n:4"] { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 640px) { .gallery.gallery--group { grid-template-columns: repeat(2, 1fr); } }
/* A short muted clip on a page, shown whole at its own shape (production page, owner 2026-10-22). */
.page-video { margin: 1.75rem 0; border: 1px solid var(--line); background: #000; }
.page-video video { width: 100%; height: auto; display: block; }
.gallery figcaption {
  padding: 0.6rem 0.85rem;
  font-size: 0.88rem;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

/* Contact block on home */
.contact-block {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: clamp(1.5rem, 5vw, 4rem);
  align-items: start;
}
@media (max-width: 820px) { .contact-block { grid-template-columns: 1fr; } }
/* Round 12 (2026-10-12): the three ways to start, above the home form, as wide as the form. */
.contact-start { min-width: 0; }
.start-ways { max-width: 560px; margin: 0 0 1.75rem; }
.start-ways li { font-size: 1rem; }

/* /book/: the owner's appointment schedule, embedded (src/book.njk). */
.booking-frame { margin: 1rem 0 1.5rem; border: 2px solid var(--line); }
.booking-frame iframe { display: block; width: 100%; height: 700px; border: 0; }

/* -------------------------------------------------------------------------- */
/* Forms                                                                     */
/* -------------------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1.1rem; }
.field label { font-weight: 700; font-size: 0.95rem; }
.field .opt, .field .hint { font-weight: 400; color: var(--ink-soft); }
.field .hint { font-size: 0.88rem; margin: 0.3rem 0 0; }
.on-black .field label { color: #f3f3f0; }
.on-black .field .hint { color: #b8b8b8; }
.field input, .field select, .field textarea {
  font: inherit;
  font-size: 1rem;   /* rem, not inherited: stays 16px even inside a smaller-type
                         panel like .quote-oversize (0.95rem context = 15.2px,
                         which iOS Safari reads as "under 16px" and zooms the
                         whole page on focus) */
  color: var(--ink);
  background: var(--white);
  border: 2px solid var(--line);
  padding: 0.7rem 0.8rem;
  width: 100%;
  max-width: 100%;
  min-width: 0;   /* let a wide <select>/file input shrink instead of forcing page overflow */
}
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { border-color: var(--yellow); }
.field textarea { resize: vertical; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.field-row > .field { min-width: 0; }   /* grid tracks default to min-width:auto and won't shrink */
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }
input[type="range"] { accent-color: var(--yellow); padding: 0; border: 0; }
.contact-form { max-width: 560px; }

/* honeypot. display:none (not off-screen): browsers only autofill fields they
   consider visible, and an autofilled honeypot silently drops a real enquiry
   or order. Bots that do not render CSS still see and fill the input. */
.hp { display: none; }

.form-status {
  margin-top: 1rem;
  padding: 0.75rem 0.95rem;
  border: 2px solid var(--line);
  font-size: 0.95rem;
}
.form-status.is-ok { border-color: #2f7d4f; background: #e9f6ee; color: #1c5334; }
.form-status.is-err { border-color: #c0506a; background: #fdecec; color: #8a1c1c; }

/* The form check's widget (card 513): Cloudflare's own frame, 300 x 65 (150 x 140 on a screen too narrow for that),
   light theme. The host stays hidden until the quote tool turns the check on. Owner, 2026-10-16: no badge on the page, so the widget is
   rendered "interaction-only" and the host takes no room (it used to hold 65px open) until a challenge for a person shows. */
.form-check { margin: 0; max-width: 100%; }
.form-check.is-challenge { margin: 0 0 1rem; min-height: 65px; }
.form-check[hidden] { display: none; }
.q-form-check { margin: 0; display: flex; justify-content: center; }
.q-check-note { margin: 0.75rem 0 0; }

/* -------------------------------------------------------------------------- */
/* Page head (interior pages)                                                */
/* -------------------------------------------------------------------------- */
.page-head { background: var(--black); text-align: center; padding-block: clamp(3rem, 8vw, 5rem); }
.page-head p { color: #d8d8d8; max-width: 60ch; margin-inline: auto; font-size: 1.1rem; }
.page-head a { color: #fff; text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; }
.page-head a:hover { color: var(--yellow); }
/* 2026-10-26 (card 720): the quote button under the summary of a service, location or industry page (partials/head-cta.njk), in the
   first screen of a phone as on Xometry's pages. It is the home hero's gold button: solid gold, then black with the gold outline on
   hover and keyboard focus. No underline from the page head's link rule above. Its corners come from .btn ("Soft corners", the end). */
.page-head-actions { display: flex; justify-content: center; margin-top: 1.5rem; }
.page-head-actions .btn { background: var(--yellow); color: var(--black); border-color: var(--yellow); text-decoration: none; }
.page-head-actions .btn:hover, .page-head-actions .btn:focus-visible { background: var(--black); color: var(--yellow); border-color: var(--yellow); }
.eyebrow {
  font-family: "Noto Sans", sans-serif;
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--yellow);
  margin: 0 0 0.9rem;
}

/* Breadcrumb trail (interior pages, inside .page-head / .cs-head on black) */
.breadcrumbs { margin: 0 0 1.1rem; }
.breadcrumbs ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.15rem 0.5rem;
  font-family: "Noto Sans", sans-serif;
  font-size: 0.78rem;   /* was 0.72rem (11.52px) -- under the 12px floor */
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
}
.breadcrumbs li { display: flex; align-items: center; gap: 0.5rem; }
.breadcrumbs li + li::before {
  content: "/";
  color: #6f6f6f;
  font-weight: 400;
}
.breadcrumbs a { color: #cfcfcf; text-decoration: none; border-bottom: 2px solid transparent; }
.breadcrumbs a:hover { color: #fff; border-bottom-color: var(--yellow); }
.breadcrumbs [aria-current="page"] { color: var(--yellow); }
/* 2026-10-26: the last crumb (the page's own title) runs as text after its slash. As a flex row, a title that wrapped on a phone (the
   industry pages, business cards) left the slash hanging at the left edge of a centred block of lines. One line looks as before. */
.breadcrumbs li:last-child { display: block; }
.breadcrumbs li:last-child::before { margin-right: 0.5rem; }

/* On a light page head (e.g. tool frames), flip the palette */
.page-head--light .breadcrumbs a { color: var(--ink-soft); }
.page-head--light .breadcrumbs a:hover { color: var(--ink); }
.page-head--light .breadcrumbs [aria-current="page"] { color: var(--heading); }
.page-head--light .breadcrumbs li + li::before { color: #b3b0a6; }

.prose { padding-block: clamp(2.5rem, 6vw, 4.5rem); }
.prose .wrap { max-width: 760px; }
.prose h2 { margin-top: 2.5rem; }
.prose h3 { margin-top: 1.75rem; }
.prose a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; }
.prose ul, .prose ol { padding-left: 1.25rem; }
.prose li { margin-bottom: 0.4rem; }
.prose li::marker { color: var(--yellow); }
.prose .table-scroll { overflow-x: auto; margin: 1.5rem 0; -webkit-overflow-scrolling: touch; }
.prose table { border-collapse: collapse; width: 100%; margin: 1.5rem 0; font-size: 0.95rem; }
.prose .table-scroll table { margin: 0; }
.prose th, .prose td { text-align: left; padding: 0.6rem 0.7rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.prose th { font-family: "Konkhmer Sleokchher", sans-serif; color: var(--ink); font-weight: 400; }
.prose .legal-meta { font-size: 0.9rem; color: var(--ink-soft); margin-top: -0.5rem; }

/* Quote page */
.quote-cols { display: grid; grid-template-columns: 1.6fr 1fr; gap: 1.5rem; align-items: start; }
@media (max-width: 900px) { .quote-cols { grid-template-columns: 1fr; } }
.quote-card { border: 2px solid var(--line); padding: clamp(1.25rem, 3vw, 2rem); }
.quote-card.alt { background: var(--black); border-color: var(--black); }
.quote-card.alt p { color: #d8d8d8; }
.quote-card.alt .field label { color: #f3f3f0; }
.quote-card.alt a { color: var(--yellow); text-decoration: underline; }
.quote-card.alt a:hover { color: #fff; }
.quote-card h2 { font-size: clamp(1.5rem, 3vw, 2rem); }
.quote-form { margin-top: 1.25rem; }
.quote-result { margin-top: 0; }
.quote-total { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.quote-total .l { font-size: 0.95rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-soft); }
.quote-total .v { font-family: "Konkhmer Sleokchher", sans-serif; font-size: clamp(1.8rem, 4vw, 2.4rem); color: var(--ink); }
/* table-layout: fixed locks column widths to the sizes below instead of
   growing to fit content (the old auto layout let one long file name push
   the whole table wider than its panel). Part gets no explicit width, so it
   receives whatever's left after Material + Line are subtracted, and wraps
   into that space instead of forcing the table to overflow. */
.quote-breakdown { width: 100%; table-layout: fixed; border-collapse: collapse; margin: 1.1rem 0 0.25rem; font-size: 0.95rem; font-variant-numeric: tabular-nums; }
.quote-breakdown th { text-align: left; font-family: "Konkhmer Sleokchher", sans-serif; font-weight: 400; color: var(--ink); font-size: 0.85rem; padding: 0 0.5rem 0.4rem; border-bottom: 1px solid var(--line); }
.quote-breakdown td { padding: 0.45rem 0.5rem; border-bottom: 1px solid var(--line); }
.quote-breakdown th, .quote-breakdown td { overflow-wrap: anywhere; word-break: break-word; }
.quote-breakdown th:nth-child(2), .quote-breakdown td:nth-child(2) { width: 4.5rem; }
.quote-breakdown th:nth-child(3), .quote-breakdown td:nth-child(3) { width: 6.75rem; }
.quote-breakdown .num { text-align: right; white-space: nowrap; }
.quote-breakdown tfoot td { color: var(--ink-soft); border-bottom: none; padding-top: 0.5rem; }
.quote-breakdown tfoot .grand td { color: var(--ink); font-family: "Konkhmer Sleokchher", sans-serif; font-size: 1.05rem; border-top: 3px solid var(--yellow); padding-top: 0.6rem; }
.quote-breakdown tfoot .q-save td { color: #1a7f37; font-weight: 600; }
.quote-error { margin-top: 1rem; color: #8a1c1c; background: #fdecec; border: 2px solid #f2b8b8; padding: 0.75rem 0.9rem; font-size: 0.95rem; }
.q-color-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.q-swatch { flex: none; width: 1.5rem; height: 1.5rem; border: 2px solid var(--line); background: transparent; }
.q-color-req { margin-top: 0.5rem; }
/* "Basic" tab in the picker: just a solid black box and a solid white box */
.sp-sw.sp-basic { max-width: 160px; }
.sp-sw.sp-basic .sp-chip { display: block; width: 100%; aspect-ratio: 1; }
.sp-sw.sp-basic .sp-nm { font-size: 0.95rem; text-align: center; padding: 0.35rem 0 0; }
/* The manual-quote panel names the files it will and will not attach: a long file name with no space has to wrap,
   not widen the panel past a phone's width (card 366, like .quote-card below). */
.quote-oversize { margin: 0 0 1rem; border: 2px solid var(--yellow); background: #fff9e0; padding: 1rem 1.1rem; font-size: 0.95rem; overflow-wrap: anywhere; }
.quote-oversize > p:first-child { font-weight: 600; margin-top: 0; }
.quote-oversize .field { margin-bottom: 0.75rem; }
.quote-oversize .q-oversize-offer { margin: 0 0 1rem; }
.quote-oversize .q-oversize-offer p { margin: 0 0 0.25rem; }
.quote-oversize .btn { margin-top: 0.5rem; }
/* Yellow-on-cream (the default .btn-secondary) measures 1.48:1 here --
   nowhere near the 4.5:1 minimum. Dark text/outline on this light panel
   instead (measures 17.44:1). */
.quote-oversize .btn-secondary { color: var(--ink); border-color: var(--ink); }
.quote-oversize .btn-secondary:hover { background: var(--ink); color: var(--white); }
/* "layer_not_available": one button per layer the resin does print at */
.q-layer-choice { display: flex; flex-wrap: wrap; gap: 0 0.6rem; }
#q-config { font-weight: 600; color: var(--ink); margin: 0.35rem 0 0.75rem; }
.q-color-label { font-size: 0.9rem; color: var(--ink-soft); min-width: 0; overflow-wrap: anywhere; }

/* keep the quote card from ever forcing horizontal page scroll on mobile */
.quote-card { min-width: 0; overflow-wrap: anywhere; }
.quote-form, .quote-result, .quote-oversize { min-width: 0; }
/* readability: one short hint per field, slightly larger, no widowed intro
   on the black custom-quote panel, no 4-line hint wrap in the 2-col grid */
.quote-cols .field .hint { font-size: 0.95rem; }
.quote-card.alt .lede { text-wrap: balance; }
@media (min-width: 640px) and (max-width: 1024px) {
  .quote-cols .field-row { grid-template-columns: 1fr; }
}

/* order cart (temporary, per visitor): steps 3 and 4 of the quote page since 2026-10-09 */
.cart-lines { list-style: none; margin: 0 0 1rem; padding: 0; }
.cart-empty { color: var(--muted); font-size: 0.92rem; }
.cart-line { display: flex; gap: 0.75rem; align-items: flex-start; justify-content: space-between;
  padding: 0.9rem 0; border-bottom: 1px solid var(--line); }
.cart-line:first-child { border-top: 1px solid var(--line); }
.cl-thumb { flex: none; width: 48px; height: 48px; object-fit: cover; background: #0a0a0b; border: 1px solid var(--line-dark); }
.cl-thumb--blank { background: repeating-linear-gradient(45deg, #ececea, #ececea 4px, #f6f6f4 4px, #f6f6f4 8px); }
.cl-main { min-width: 0; flex: 1; }
.cl-title { font-weight: 700; }
.cl-sub { font-size: 0.88rem; color: var(--ink-soft); margin-top: 0.15rem; text-wrap: balance; }
.cl-right { flex: none; text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 0.4rem; }
/* A custom item's plain status ("Manually quoted"). Lines carry no price. */
.cl-status { font-size: 0.85rem; color: var(--muted); white-space: nowrap; }
.cl-qty { display: flex; align-items: center; gap: 0.3rem; }
.cl-qty-btn { width: 26px; height: 26px; line-height: 1; font: inherit; font-size: 1rem; font-weight: 700;
  background: var(--panel); border: 1px solid var(--line); border-radius: 4px; color: var(--ink); cursor: pointer; }
.cl-qty-btn:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--hover); }
.cl-qty-btn:disabled, .cl-qty-btn[aria-disabled="true"] { opacity: 0.5; cursor: default; }
.cl-qty-input { width: 3.4em; text-align: center; font: inherit; font-size: 0.9rem;
  border: 1px solid var(--line); border-radius: 4px; padding: 0.2rem 0.1rem; background: var(--panel); color: var(--ink); }
.cl-qty-input:disabled, .cl-qty-input[aria-disabled="true"] { opacity: 0.6; }
.cl-qty-plain { font-size: 0.82rem; color: var(--ink-soft); }
.cl-qty-err { font-size: 0.78rem; color: #8a1c1c; max-width: 12rem; text-align: right; }
/* Round 16 (2026-10-12): the line under a row at the 49-piece cap, and the same line under step 2's Quantity row. */
.cl-qty-cap { font-size: 0.78rem; color: var(--ink-soft); max-width: 12rem; text-align: right; margin-top: 0.3rem; }
.q-custom-link { color: var(--heading); text-decoration: underline; text-decoration-color: var(--yellow);
  text-decoration-thickness: 2px; text-underline-offset: 3px; }
.q-custom-link:hover { color: var(--ink); }
.cl-remove { font: inherit; font-size: 0.82rem; background: none; border: 0; color: var(--muted);
  text-decoration: underline; cursor: pointer; padding: 0; }
.cl-remove:hover { color: var(--ink); }
.cl-reattach { display: inline-flex; flex-direction: column; gap: 0.25rem; margin-top: 0.5rem;
  font-size: 0.82rem; color: #8a1c1c; }
.cl-reattach input { font: inherit; font-size: 0.82rem; }
/* Card 294: a line whose quote the server no longer has, flagged in its own row
   (cart.js _gone), and the inline fallback under the lines when no single line
   could be named (_goneUnknown). Same red as the other cart errors. */
.cl-gone { margin-top: 0.4rem; font-size: 0.85rem; font-weight: 600; color: #8a1c1c; }
/* Card 304: the one plain line under the same rule, for a quote the server refused
   on a reprice with no row to show it under (cart.js _priceNote; cards 304, 326, 331):
   advice, not a block. */
.cs-gone, .cs-requote { margin: 0 0 0.6rem; font-size: 0.9rem; font-weight: 600; color: #8a1c1c; }
.cart-summary { margin: 0.5rem 0 0.75rem; }
.cs-row { display: flex; justify-content: space-between; padding: 0.3rem 0; font-size: 0.95rem; color: var(--ink-soft); }
.cs-row.cs-total { border-top: 3px solid var(--yellow); margin-top: 0.35rem; padding-top: 0.6rem;
  color: var(--ink); font-family: "Konkhmer Sleokchher", sans-serif; font-size: 1.1rem; }
.cs-total small { font-family: "Noto Sans", sans-serif; font-size: 0.8rem; color: var(--muted); }
/* Owner, 2026-10-24: the one sentence under the total when the order minimum binds (cart.js), in place of a "Minimum order" line.
   Set like the shipping rule and the checkout note under it (0.8rem, ink, left), so the three read as one column. */
.cs-minnote { margin: 0.35rem 0 0; font-size: 0.8rem; color: var(--ink); }
/* Round 2 review, should-fix (2026-09-27): the order-level "Pricing…"
   placeholder (cart.js's _pricingUnresolved()) reads as a question mark,
   not a real number -- muted, plain weight, same family as everything
   else, so it never looks like a confidently-stated total. */
.cs-row.cs-total.cs-pricing { color: var(--muted); font-family: "Noto Sans", sans-serif; font-weight: 400; }
#cart-checkout { display: flex; flex-wrap: wrap; gap: 0.6rem; }
#cart-contact { margin-top: 1.25rem; border-top: 1px solid var(--line); padding-top: 1.25rem; }

/* Fulfilment choice (pickup/ship) in the cart's checkout step. Stacked, one
   full-width row per option -- with no rule here at all the two <label>s
   laid out as default inline content and ran together on one line, worst
   at phone width and inside the drawer's own narrow column (review finding
   M2, 2026-09-26). */
.cl-fulfilment { border: 2px solid var(--line); border-radius: 4px; padding: 0.2rem 0 0.3rem; margin: 0 0 1.1rem; }
.cl-fulfilment legend { font-weight: 700; font-size: 0.95rem; padding: 0 0.5rem; margin-left: 0.3rem; }
.cl-fulfilment label {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  width: 100%;
  padding: 0.7rem 0.9rem;
  font-size: 0.92rem;
  line-height: 1.4;
  color: var(--ink);
  cursor: pointer;
}
.cl-fulfilment label + label { border-top: 1px solid var(--line); }
.cl-fulfilment input[type="radio"] {
  flex: 0 0 auto;
  margin-top: 0.2rem;
  width: 1.1rem;
  height: 1.1rem;
  accent-color: var(--yellow);
}
.cl-fulfilment input[type="radio"]:focus-visible {
  outline: 2px solid var(--yellow);
  outline-offset: 2px;
}
@media (max-width: 480px) {
  /* Larger tap target once the drawer is full-width on a phone. */
  .cl-fulfilment label { padding: 0.9rem; }
}
@media (max-width: 480px) {
  .cart-line { flex-direction: column; gap: 0.4rem; }
  .cl-right { text-align: left; align-items: flex-start; flex-direction: row; flex-wrap: wrap; gap: 1rem; }
  .cl-qty-err, .cl-qty-cap { text-align: left; max-width: none; }
}

/* ===========================================================================
   Quote tool "Workshop instrument" direction (Phase A). Scoped to this
   page's own components only: white viewer (near-black until 2026-10-09), off-white rail,
   yellow reserved for state + the one primary CTA, monospace uppercase
   spec labels with tabular numerals, 150ms fades, values count up, nothing
   bounces. Does not touch the site-wide look.
   =========================================================================== */
.q-mono { font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; font-variant-numeric: tabular-nums; }
.q-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* No page-head hero on this page (removed): the sample has none, and it
   pushed the whole tool 401px down. The page's only h1 is visually hidden
   (the black title bar that showed it came off on 2026-10-25); page
   <title>/meta description are unchanged (frontmatter); the
   visible breadcrumb nav is gone but the BreadcrumbList JSON-LD in
   partials/schema.njk is emitted independently in <head> from the
   breadcrumbs filter, not from the visible partial, so it's unaffected. */
.q-instrument-compact { padding-block: 0 clamp(1.5rem, 4vw, 2.5rem); }
.q-instrument-compact .q-wrap-wide { padding-block-start: 1.25rem; }

/* The Order button, at the right end of the step row on the white page: dark text and a dark outline like the page's other
   outline buttons (no yellow text on white), yellow on hover. */
.cart-toggle { position: relative; flex: none; display: inline-flex; align-items: center; gap: 0.5rem; height: 40px;
  padding: 0 0.9rem; background: var(--panel); border: 2px solid var(--ink); color: var(--ink);
  font: inherit; font-size: 0.9rem; font-weight: 600; line-height: 1; letter-spacing: 0.03em; white-space: nowrap; cursor: pointer; }
.cart-toggle:hover { background: var(--yellow); border-color: var(--yellow); color: var(--black); }
.cart-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 1.3rem; height: 1.3rem;
  padding: 0 0.3rem; background: var(--yellow); color: var(--black); font-family: ui-monospace, monospace;
  font-size: 0.78rem; font-weight: 700; border-radius: 50%; }
.cart-toggle:hover .cart-badge { background: var(--black); color: var(--yellow); }

/* ===========================================================================
   Quote tool, dense-form layout (2026-09-19). Xometry-style engineering
   form in Crafted's own look: near-black chrome, off-white canvas, yellow
   only on the CTA and the active step, monospace caps labels (the corners
   were square until 2026-10-24). Built from the approved sample
   C:\Users\nlewi\crafted-ops\specs\quote-layout\quote-layout-sample.dc.html
   (updated 2026-09-19 for the column-order change: form left, price +
   preview right). Structure only was taken from the sample -- copy stays
   data-driven from /api/catalog + MATERIAL_INFO, never hardcoded.
   ========================================================================== */
.q-wrap-wide { max-width: 1368px; margin-inline: auto; padding-inline: var(--pad); }

/* Step 1's sample buttons (shown on a ?mock=1 or ?samples=1 address only). */
.q-tb-sample { font: inherit; font-size: 0.78rem; padding: 0.28rem 0.6rem; background: var(--bg); border: 1.5px solid var(--line);
  cursor: pointer; transition: border-color 150ms ease, background 150ms ease; }
.q-tb-sample:hover { border-color: var(--yellow); background: var(--hover); }
.q-tb-sample:disabled { opacity: 0.6; cursor: wait; }

/* --- the quote's four steps (owner, 2026-10-25: the black "Instant quote" bar came off, its steps did not read as links). One
   row at the top of the white page, numbered circles joined by a thin line, the Order button at its right end. The step on
   screen: a filled yellow circle and a bold name. A step that can be reached (back, or ahead once something is on it): ink, a
   pointer and an underline on hover. A step that cannot yet: grey, a disabled button. The line runs yellow up to the step on
   screen. Narrower than 860px only the step on screen keeps its name (the numbers stay in one row); narrower than 520px the
   Order button takes the line above the steps, which then span the width. The line is what gives way on a narrow screen: its
   length comes from the screen width (clamp), worked out so the row fits with the longest name on screen ("Review order") and a
   two-digit count on the Order button, at the 4vw page gutter. Not sticky: the bar it replaces scrolled with the page. --- */
.q-stepbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.5rem; padding-bottom: 0.9rem; margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--line); }
.q-step-list { list-style: none; margin: 0; padding: 0; display: flex; align-items: center; }
.q-step-item { display: flex; align-items: center; flex: none; }
.q-step-item + .q-step-item::before { content: ""; flex: none; width: clamp(0.75rem, 30vw - 230px, 2.5rem); height: 2px; margin-inline: 0.65rem;
  background: var(--line); }
.q-step-item.is-done::before, .q-step-item.is-current::before { background: var(--yellow); }
.q-step-btn { flex: none; display: inline-flex; align-items: center; gap: 0.55rem; margin: 0; padding: 0.3rem 0.15rem; background: none; border: 0;
  font: inherit; font-size: 0.95rem; font-weight: 400; line-height: 1.2; color: var(--ink); white-space: nowrap; cursor: pointer; }
.q-step-n { flex: none; width: 1.8rem; height: 1.8rem; display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid currentColor; border-radius: 50%; background: var(--panel); font-size: 0.82rem; font-weight: 700;
  font-variant-numeric: tabular-nums; transition: background-color 150ms ease; }
@media (hover: hover) {
  .q-step-btn:not(:disabled):not([aria-current]):hover .q-step-name { text-decoration: underline; text-decoration-color: var(--yellow);
    text-decoration-thickness: 2px; text-underline-offset: 4px; }
  .q-step-btn:not(:disabled):not([aria-current]):hover .q-step-n { background: var(--hover); }
}
.q-step-btn[aria-current="step"] { font-weight: 700; cursor: default; }
.q-step-btn[aria-current="step"] .q-step-n { background: var(--yellow); border-color: var(--yellow); color: var(--black); }
.q-step-btn:disabled { color: #9a9a92; cursor: default; }
.q-step-btn:focus-visible { outline: 3px solid var(--yellow); outline-offset: 2px; }
.q-stepbar .cart-toggle { margin-left: auto; }
@media (max-width: 859px) {
  .q-step-item + .q-step-item::before { width: clamp(0.75rem, 30vw - 140px, 2.5rem); margin-inline: 0.4rem; }
  .q-step-item:not(.is-current) .q-step-name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }
}
@media (max-width: 519px) {
  .q-stepbar { row-gap: 0.6rem; }
  .q-stepbar .cart-toggle { order: -1; }
  .q-step-list { flex-basis: 100%; }
  .q-step-item:not(:first-child) { flex: 1 1 auto; }
  .q-step-item + .q-step-item::before { flex: 1 1 auto; width: auto; min-width: 0.5rem; margin-inline: 0.3rem; }
  .q-step-n { width: 1.6rem; height: 1.6rem; }
}

/* The lead time card on step 3 keeps its own bottom margin. */
.q-leadtime-card { margin-bottom: 1rem; }

/* --- main grid: form (flexible) | price + preview (fixed, sticky) --- */
.q-app { display: grid; grid-template-columns: minmax(0, 1fr) 480px; gap: 1.5rem; align-items: start; margin-top: 0; }
@media (max-width: 1099px) {
  .q-app { grid-template-columns: 1fr; }
}

/* --- left: the configure form + the two mini cards under it --- */
.q-form-col { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.q-config-card { background: var(--panel); border: 1px solid var(--line); padding: 1rem 1.25rem; min-width: 0; }
/* While the oversize panel is showing, the Configure card (Print orientation is in it) sits ABOVE it, so the customer can turn the part
   first (card 490). The panel comes first in the markup and is hidden otherwise, so nothing moves when it is not showing. */
.q-form-col:has(> #quote-oversize:not([hidden])) > .q-config-card { order: -1; }
.q-config-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.4rem 1rem; margin-bottom: 0.85rem; }
.q-config-head h2 { margin: 0; font-size: 1.05rem; }
.q-link-btn { font: inherit; font-size: 0.85rem; background: none; border: 0; padding: 0; color: var(--heading);
  text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; cursor: pointer; }
.q-link-btn:hover { color: var(--ink); }
.q-link-btn--sm { font-size: 0.72rem; margin-left: 0.5rem; text-decoration-thickness: 1.5px; }

.q-config-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem 1.25rem; }
.q-config-grid .field { margin-bottom: 0; min-width: 0; gap: 0.3rem; }
.q-config-grid .field .hint { font-size: 0.8rem; line-height: 1.35; }
.q-field--full { grid-column: 1 / -1; }
/* Quantity and model units share a row, each as wide as it needs to be: the quantity box fits a few digits and no more, the
   units toggle takes the rest so "Millimeters" and "Inches" show whole (owner, 2026-10-10: they had a half each, and the
   toggle clipped to "Millimet..." beside a box wide enough for billions). */
/* The units switch takes the rest of the row while it has 12rem; on a phone it drops under the quantity at full width, so
   "Millimeters" never cuts off (2026-10-24). */
.q-qty-units { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.6rem 1.25rem; }
.q-qty-units .field { margin-bottom: 0; min-width: 0; }
.q-qty-units .q-field-qty { flex: 0 0 7rem; }
.q-qty-units .q-field-units { flex: 1 1 12rem; }
.q-qty-units .q-seg-btn { flex: 1 1 auto; padding: 0 1rem; }
.q-config-grid > .q-qty-cap { font-size: 0.8rem; line-height: 1.35; margin: 0; color: var(--ink-soft); }
/* minmax(0, 1fr), not 1fr: a plain 1fr column grows to its widest control (the material list's longest option), and on a phone
   every row ran 18 px into the card's right padding (2026-10-24). */
@media (max-width: 560px) { .q-config-grid { grid-template-columns: minmax(0, 1fr); } }
/* height:44px (border-box) matches the color button / segmented controls
   for row alignment, but the shared .field rule's padding-block (0.7rem =
   11.2px per side) plus this 2px border leaves only a 17.6px content box --
   shorter than a 16px Noto Sans normal line box (~21.6px), so descenders on
   "Nylon", "Polycarbonate", "PETG" etc. clip in browsers that don't forgive
   it the way Chrome/Windows headless does. padding-block:0 keeps the same
   44px box and horizontal padding, just gives the line box the room it
   needs; native select/input rendering centers the text in what's left. */
.q-config-grid select, .q-config-grid input[type="number"] { height: 44px; padding-block: 0; }
.q-config-grid label { display: flex; align-items: center; font-family: ui-monospace, monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); font-weight: 400; }
.q-config-grid > .field > span:first-child { font-family: ui-monospace, monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }

.q-color-field { height: 44px; width: 100%; padding: 0 0.75rem; border: 2px solid var(--line); background: var(--white);
  display: flex; align-items: center; gap: 0.6rem; font: inherit; font-size: 0.95rem; color: var(--ink); cursor: pointer; text-align: left; }
.q-color-field:hover { border-color: var(--yellow); }
.q-color-field .q-swatch { flex: none; width: 1.25rem; height: 1.25rem; border: 1px solid var(--line); background: transparent; }
.q-color-field .q-color-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); }
.q-color-choose { flex: none; font-size: 0.78rem; color: var(--muted); }

.q-seg { display: flex; width: 100%; border: 2px solid var(--line); }
.q-seg-btn { flex: 1 1 0; min-width: 0; height: 44px; font: inherit; font-size: 0.85rem; padding: 0 0.4rem; background: var(--panel);
  border: 0; border-right: 2px solid var(--line); cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: background 150ms ease, color 150ms ease; }
.q-seg-btn:last-child { border-right: 0; }
.q-seg-btn.is-selected { background: var(--ink); color: var(--white); font-weight: 700; }
.q-seg-btn:hover:not(.is-selected) { background: var(--hover); }
.q-seg-btn.is-disabled, .q-seg-btn:disabled, .q-seg-btn[aria-disabled="true"] { color: var(--muted); cursor: not-allowed; opacity: 0.55; }
.q-seg-btn.is-disabled:hover, .q-seg-btn:disabled:hover, .q-seg-btn[aria-disabled="true"]:hover { background: var(--panel); }
/* Review should-fix 5 (2026-09-27): a long label would ellipsize at narrow widths
   under the base .q-seg-btn's fixed 44px/nowrap rule above, so these controls
   wrap instead (Strength too since 2026-10-24: "Max strength" cut off on a 320 px
   phone). Orientation and Units keep their short labels and fixed single-line height. */
#q-tech-seg .q-seg-btn, #q-strength-seg .q-seg-btn, #q-leadtime-seg .q-seg-btn { height: auto; min-height: 44px; white-space: normal; overflow: visible;
  text-overflow: clip; line-height: 1.25; padding: 0.5rem 0.6rem; }
/* SL11: the lead-time tier buttons carry a short sub-caption ("5 business
   days", "+25%") under the tier label, from the catalog -- never hardcoded. */
#q-leadtime-seg .q-seg-sub { display: block; font-size: 0.75rem; font-weight: 400; opacity: 0.75; }
/* S27 (2026-10-14): the lead time chooser is one row per tier, full width, in the segment's own colours and selected state: the
   label and the days on the left, the order's total at that tier on the right (the owner: "the way Xometry does"). */
#q-leadtime-seg { flex-direction: column; }
#q-leadtime-seg .q-seg-btn { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  width: 100%; text-align: left; border-right: 0; border-bottom: 2px solid var(--line); }
#q-leadtime-seg .q-seg-btn:last-child { border-bottom: 0; }
#q-leadtime-seg .q-lt-name { min-width: 0; }
#q-leadtime-seg .q-lt-total { flex: none; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-weight: 700;
  white-space: nowrap; text-align: right; }

/* --- manual orientation: the X/Y/Z rotation panel under the segmented control --- */
.q-rotation-panel { margin-top: 0.6rem; padding: 0.7rem; border: 2px solid var(--line); background: var(--panel); }
.q-rot-row { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; }
.q-rot-row:first-of-type { margin-top: 0.35rem; }
.q-rot-axis { flex: none; width: 1.1rem; font-weight: 700; text-align: center; }
.q-rot-btn { flex: none; height: 34px; padding: 0 0.5rem; font: inherit; font-size: 0.78rem; background: var(--white);
  border: 2px solid var(--line); cursor: pointer; transition: background 150ms ease; }
.q-rot-btn:hover { background: var(--hover); }
.q-rotation-panel input[type="number"] { flex: 1 1 auto; min-width: 0; height: 34px; text-align: center; }
/* "Use this orientation" (owner, 2026-10-25): the panel's full width under the rows, Save part's height (.q-btn-primary); grey,
   like Save part's off state, while the turn on screen is the one priced. */
.q-rot-confirm { display: block; width: 100%; margin-top: 0.7rem; }
.q-rot-confirm[aria-disabled="true"], .q-rot-confirm[aria-disabled="true"]:hover { background: var(--line); color: var(--ink-soft); cursor: default; }

/* --- the two mini cards under the form: Your order / custom quote --- */
.q-side-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
@media (max-width: 560px) { .q-side-cards { grid-template-columns: 1fr; } }
.q-mini-card { background: var(--panel); border: 1px solid var(--line); padding: 0.85rem 1.1rem; display: flex; flex-direction: column; gap: 0.45rem; min-width: 0; }
.q-mini-card h2 { margin: 0; font-size: 0.95rem; }
.q-mini-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.q-mini-card-tag { font-family: ui-monospace, monospace; font-size: 0.68rem; letter-spacing: 0.06em; color: var(--muted); white-space: nowrap; }
.q-mini-card-p { margin: 0; font-size: 0.85rem; color: var(--ink-soft); }
.q-btn-outline, .q-btn-dark { height: 40px; font: inherit; font-size: 0.88rem; font-weight: 600; cursor: pointer; border: 1.5px solid var(--ink); }
.q-btn-outline { background: var(--panel); color: var(--ink-soft); border-color: var(--line); }
.q-btn-outline:hover { border-color: var(--ink); color: var(--ink); }
.q-btn-dark { background: var(--panel); color: var(--ink); }
.q-btn-dark:hover { background: var(--ink); color: var(--white); }
.q-custom-field { margin-bottom: 0; }
.q-custom-field textarea { font-size: 0.88rem; padding: 0.5rem 0.6rem; }
.q-custom-field input[type="file"] { font-size: 0.8rem; padding: 0.4rem 0.5rem; }

/* --- right: price card + preview + progress, sticky under the site header. --- */
.q-side-col { display: flex; flex-direction: column; gap: 1rem; min-width: 0;
  position: sticky; top: calc(96px + 1rem); }
/* Below the .q-app single-column breakpoint (1099px, above) the side
   column moves ABOVE the form (order: -1) and must give up being sticky --
   stacked in one column, a still-sticky side column stops scrolling partway
   down the page while its own reserved grid row stays sized for its
   original position, so the form below renders up under it instead of
   after it (measured: the Color field overlapping the progress ticks at
   375px). This rule has to come AFTER the base .q-side-col rule above, not
   inside the earlier @media block next to `.q-app { grid-template-columns:
   1fr; }` -- same selector specificity, so source order decides, and the
   earlier position was silently losing to the unconditional `position:
   sticky` here on every width, not just mobile. */
@media (max-width: 1099px) {
  .q-side-col { position: static; height: auto; max-height: none; order: -1; }
}

.q-price-panel { background: var(--panel); border: 1px solid var(--line); border-top: 3px solid var(--yellow);
  padding: 1.1rem 1.25rem; flex: none; min-width: 0; overflow-wrap: anywhere; }
/* Before a quote exists, #quote-result is [hidden] -- collapse the frame
   around it instead of showing an empty bordered box. */
.q-price-panel:has(#quote-result[hidden]) { display: none; }
.q-price-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.q-estimate-sub { font-family: ui-monospace, monospace; font-size: 0.7rem; letter-spacing: 0.06em; color: var(--muted); white-space: nowrap; }
#q-config-detail { font-size: 0.78rem; color: var(--muted); text-align: right; text-wrap: balance; } /* card 821, with text.js keepTogether() */
/* Card 821: under 360 px the line beside "Price · Qty 1" is 142 px, too narrow for "Tough 2000 V2, Standard"; it goes under the label. */
@media (max-width: 359px) { .q-price-head { flex-wrap: wrap; row-gap: 0.2rem; } #q-config-detail { flex-basis: 100%; text-align: left; } }
.q-price-row { display: flex; align-items: center; gap: 1rem; margin-top: 0.5rem; }
.q-price-row .v { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-weight: 700;
  font-size: 2.1rem; color: var(--ink); line-height: 1; white-space: nowrap; }
.q-btn-primary, .q-price-row #q-add { flex: 1 1 auto; height: 48px; border: 0; background: var(--yellow); color: var(--black);
  font: inherit; font-size: 0.95rem; font-weight: 700; cursor: pointer; transition: background 150ms ease; }
.q-btn-primary:hover, .q-price-row #q-add:hover { background: var(--yellow-hi); }
.q-updating-label { display: none; font-family: ui-monospace, monospace; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin-top: 0.4rem; }
.q-price-panel.is-updating #quote-result { opacity: 0.55; transition: opacity 150ms ease; }
.q-price-panel.is-updating .q-updating-label { display: inline-block; }
.q-flash { animation: qFadeIn 150ms ease; }
@keyframes qFadeIn { from { opacity: 0.4; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .q-flash { animation: none; } }

/* The fine print under the price: the shipping rule, then the firm-price line (site audit c3d-commerce-35; the owner's
   sentence is two) and the lead time line. The "How this price is built" list came off in owner brief step 4 (2026-10-11). */
.q-price-foot { display: flex; flex-direction: column; align-items: flex-start; gap: 0.45rem;
  border-top: 1px solid var(--line); margin-top: 0.6rem; padding-top: 0.6rem; }
.q-fineprint { flex: none; margin: 0; font-size: 0.76rem; line-height: 1.45; color: var(--muted); }
/* 2026-10-12 (card 619): "Review order" inside step 2's note when the lead time is fixed reads at the note's own size. */
.q-fineprint .q-link-btn { font-size: inherit; text-decoration-thickness: 1.5px; text-underline-offset: 2px; }

/* --- 3D viewer: the hero. White ground (owner 2026-10-09, was near-black),
   light grey grid, drag to orbit. The overlays (facts, fit badge, wireframe
   toggle, footer strip) are dark-on-light to match.
   Height is a fixed 220px by default (the same floor the one-screen budget
   at 1280x800 needs, verified exactly) with a height media query bumping
   it up on tall viewports instead of one vh clamp shared across every
   breakpoint -- a single shared clamp meant the tight 1280x800 budget was
   always the ceiling, so 1440x900 never got to use its spare room. 860px
   was chosen as the switch point because it's above the 800px short case
   and below 900px, so 1280x800 keeps today's exact 220px behavior and
   900px-tall viewports (1440x900 included) get the taller box.
   viewer.js's own ResizeObserver on this box updates the three.js camera
   aspect + renderer size on every resize, so the render never stretches. --- */
/* DFM checks step 1 (2026-10-24): the viewer column stacks the viewer and, under it, the checks box. */
.q-hero { position: relative; min-width: 0; display: flex; flex-direction: column; gap: 1rem; }
.q-viewer { position: relative; width: 100%; height: 220px; background: var(--white); border: 1px solid var(--line);
  display: flex; flex-direction: column; overflow: hidden; touch-action: none; }
@media (min-height: 860px) {
  .q-viewer { height: clamp(320px, 34vh, 420px); }
}
.q-viewer.is-dragover { outline: 2px dashed var(--yellow); outline-offset: -2px; }
.q-viewer-canvas { position: absolute; inset: 0 0 32px; }
.q-viewer-canvas canvas { display: block; width: 100% !important; height: 100% !important; cursor: grab; }
.q-viewer-canvas canvas:active { cursor: grabbing; }
.q-viewer-empty { position: absolute; inset: 0 0 32px; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 0.6rem; color: var(--ink-soft); text-align: center; padding: 1rem; z-index: 1; }
.q-viewer-empty p { margin: 0; font-size: 0.95rem; pointer-events: none; }
.q-viewer-empty-hint { font-size: 0.78rem !important; color: var(--muted); max-width: 30rem; }
.q-viewer-empty-hint a { color: var(--ink); }
/* Card 550: the one line over the empty viewer while a STEP file is read, or when it has no preview. Clicks and drags go through. */
.q-viewer-status { pointer-events: none; }
/* The Shaded | Wireframe switch: both always visible, a yellow thumb slides under the one that is on. */
.q-view-toggle { position: absolute; top: 0.6rem; right: 0.6rem; z-index: 2; display: grid; grid-template-columns: 1fr 1fr;
  background: rgba(255,255,255,0.92); border: 1px solid #c4c4c8; border-radius: 999px; padding: 3px; box-shadow: 0 1px 4px rgba(0,0,0,0.12); }
.q-view-thumb { position: absolute; top: 3px; bottom: 3px; left: 3px; width: calc(50% - 3px); border-radius: 999px; background: var(--yellow);
  box-shadow: 0 1px 3px rgba(0,0,0,0.25); transition: transform 240ms cubic-bezier(0.4, 0, 0.2, 1); }
.q-view-toggle[data-view="wireframe"] .q-view-thumb { transform: translateX(100%); }
.q-view-toggle button { position: relative; z-index: 1; background: none; border: 0; border-radius: 999px; cursor: pointer; color: #6b6b70;
  font-family: ui-monospace, monospace; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; padding: 0.4rem 0.95rem;
  transition: color 180ms ease; }
.q-view-toggle button[aria-pressed="true"] { color: var(--black); font-weight: 700; }
.q-view-toggle button:not([aria-pressed="true"]):hover { color: var(--black); }
.q-view-toggle button:focus-visible { outline: 2px solid var(--black); outline-offset: 1px; }
@media (prefers-reduced-motion: reduce) { .q-view-thumb { transition: none; } }
.q-fit-badge { position: absolute; top: 0.6rem; right: 0.6rem; z-index: 2; margin-top: 2.6rem;
  padding: 0.2rem 0.5rem; border: 1px solid #c4c4c8; font-family: ui-monospace, monospace; font-size: 0.68rem;
  letter-spacing: 0.05em; color: var(--ink-soft); background: rgba(255,255,255,0.85); white-space: nowrap; }
.q-fit-badge.is-oversize { color: #a8281a; border-color: #e0493a; }
/* No "Fits" in the preview (owner, 2026-10-10). The red "Too large" stays: that one tells the customer something. */
.q-fit-badge:not(.is-oversize) { display: none; }

.q-file-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* facts overlay: top-left, dark text on the white ground, no title (the
   two lines are self-explanatory at a glance). Both units on each line since 2026-10-11, so it is wider; a unit group
   never breaks inside itself, a narrow viewer breaks the line at the dot. */
.q-facts-overlay { position: absolute; top: 0.6rem; left: 0.6rem; z-index: 1; width: min(380px, 70%); }
.q-fact-u { white-space: nowrap; }
.q-fact { padding: 0.12rem 0; font-size: 0.72rem; }
.q-fact-l { display: block; font-family: ui-monospace, monospace; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); font-size: 0.66rem; white-space: nowrap; }
.q-fact-v { display: block; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; color: var(--ink); text-shadow: 0 0 4px rgba(255,255,255,0.9); }
.q-fact-v .q-fact-v2 { display: block; color: var(--ink-soft); font-size: 0.92em; }

/* Card 588, DFM checks step 1 (2026-10-24; owner: "yes, start step 1 of the DFM checks", after a competitor's "8/8 DFM Checks
   Complete" box): the checks for the part on screen, in a box under the viewer that opens and shuts. The structure is the
   reference's (a head that counts, one ruled line per check), the look is the site's: a box on --radius and --shadow-box (the
   list at the end of this file), the head in the heading type. The marks are round: a tick is black on yellow, a warning a yellow
   "!" on black, a stop a white cross on the red of the "Too large" badge (no yellow text on white); the head carries the worst
   line's mark. A warning or a stop line is tinted and its label bold, so it never reads as a pass. Under the viewer at every width,
   so the model is never covered. Dimmed while the next answer is on its way. */
.q-checks { background: var(--panel); border: 1px solid var(--line); transition: opacity 150ms ease; }
.q-checks.is-updating { opacity: 0.45; }
.q-checks-head { display: flex; align-items: center; gap: 0.65rem; min-height: 48px; padding: 0.55rem 1rem; list-style: none;
  cursor: pointer; border-radius: var(--radius); }
.q-checks-head::-webkit-details-marker { display: none; }
.q-checks-head::marker { content: ""; }
.q-checks-head:hover .q-checks-title { text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px;
  text-underline-offset: 4px; }
.q-checks-head:focus-visible { outline: 2px solid var(--black); outline-offset: 2px; }
.q-checks[open] > .q-checks-head { border-bottom: 1px solid var(--line); border-radius: var(--radius) var(--radius) 0 0; }
.q-checks-title { flex: 1 1 auto; min-width: 0; font-family: "HeadingPunct", "Konkhmer Sleokchher", system-ui, sans-serif;
  font-size: 1.05rem; line-height: 1.25; color: var(--heading); }
.q-checks-caret { flex: none; width: 0.55rem; height: 0.55rem; margin: 0 0.25rem 0.25rem; border-right: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink); transform: rotate(45deg); transition: transform 150ms ease; }
.q-checks[open] .q-checks-caret { margin: 0.25rem 0.25rem 0; transform: rotate(-135deg); }
@media (prefers-reduced-motion: reduce) { .q-checks-caret { transition: none; } }
.q-checks-list { list-style: none; margin: 0; padding: 0; }
.q-check { display: grid; grid-template-columns: 1.35rem minmax(0, 1fr); column-gap: 0.65rem; row-gap: 0.1rem; align-items: center;
  padding: 0.6rem 1rem; font-size: 0.9rem; line-height: 1.35; color: var(--ink); }
.q-check + .q-check { border-top: 1px solid var(--line); }
.q-check:last-child { border-radius: 0 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px); }
.q-check-mark, .q-checks-mark { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 1.35rem;
  height: 1.35rem; border-radius: 50%; font-family: "Noto Sans", system-ui, sans-serif; font-size: 0.75rem; font-weight: 700; line-height: 1; }
.q-check-mark { grid-column: 1; grid-row: 1; }
.q-check-label, .q-check-why, .q-check-go { grid-column: 2; min-width: 0; }
.q-check-why { font-size: 0.85rem; color: var(--ink-soft); }
.q-check-go { justify-self: start; text-decoration-color: var(--ink); }
.q-check.is-pass .q-check-mark, .q-checks.is-pass .q-checks-mark { background: var(--yellow); color: var(--black); }
.q-check.is-pass .q-check-mark::before, .q-checks.is-pass .q-checks-mark::before { content: "\2713"; }
.q-check.is-warn { background: var(--hover); }
.q-check.is-warn .q-check-mark, .q-checks.is-warn .q-checks-mark { background: var(--black); color: var(--yellow); }
.q-check.is-warn .q-check-mark::before, .q-checks.is-warn .q-checks-mark::before { content: "!"; }
.q-check.is-stop { background: #fdecec; }
.q-check.is-stop .q-check-mark { background: #a8281a; color: var(--white); }
.q-checks.is-stop .q-checks-mark { background: #a8281a; color: var(--white); }
.q-check.is-stop .q-check-mark::before, .q-checks.is-stop .q-checks-mark::before { content: "\2715"; }
.q-check.is-warn .q-check-label, .q-check.is-stop .q-check-label { font-weight: 700; }
.q-check.is-stop .q-check-label { color: #8a1c1c; }
/* A note (the pieces answered "One part") carries no mark: the circle keeps its place so the label lines up with the others. */
.q-check.is-note .q-check-mark { visibility: hidden; }

.q-viewer-foot { flex: none; height: 32px; margin-top: auto; padding: 0 0.7rem; border-top: 1px solid var(--line); background: #f6f6f3;
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; font-size: 0.74rem; color: var(--muted); }
.q-orientation-note { margin: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* With nothing to say (no orientation note: a resin part, or before an answer) the foot draws nothing, and keeps its 32 px so the
   model's canvas never changes size. */
.q-viewer-foot:has(> .q-orientation-note[hidden]) { background: none; border-top-color: transparent; }
/* The note is about the pose last priced: off while a turn not priced yet is on screen (main.js renderPoseConfirm()), and the
   foot draws nothing then, as it does with no note. */
.q-orientation-note.is-stale { display: none; }
.q-viewer-foot:has(> .q-orientation-note.is-stale) { background: none; border-top-color: transparent; }

/* A viewer too narrow for the readout and the switch side by side (2026-10-26): the size readout sits at the bottom of the canvas,
   across its width, in the room the framing leaves under the part. viewer.js _frame() puts the part's bounding sphere in the middle
   57.6% of the canvas height at any turn of the part, so the bottom 56 px of a phone's 266 px canvas stay clear of it (more on a
   taller canvas); three lines here take 55 (3 x 0.72rem x 1.4, plus 0.4rem), so even a narrow phone, or a wider monospace face than
   Windows' own, does not reach the part. At the top left, 220 px wide, it wrapped to four lines: beside the Shaded | Wireframe switch
   it ran under the switch, and moved under the switch (card 694) it ran over the part. Its left edge is the orientation note's under
   it (the foot's 0.7rem). Across the width, drags that start on it turn the part, as on the status line (card 550).
   Card 801: placed by the viewer's own width, not the screen's. The readout's box at the top left is up to 380 px wide and the switch
   about 196, each 0.6rem in from its side (380 + 196 + 19.2 = 595): they fit side by side in a viewer whose content box is wider
   than 595 px, as at 1440 (598). In a narrower one the switch hid up to 74 px of the first line on a tablet or a small laptop (821 to
   975 px screens and 1200 to 1270, the viewer 428 to 502 px wide), so the bottom placement is used there, phones included. */
.q-viewer { container: q-viewer / inline-size; }
@container q-viewer (max-width: 595px) {
  .q-facts-overlay { top: auto; bottom: calc(32px + 0.4rem); left: 0.7rem; right: 0.7rem; width: auto; pointer-events: none; }
  .q-facts-overlay .q-fact { padding: 0; line-height: 1.4; }
}
/* A browser without container queries (Safari before 16) keeps the phone rule by screen width, the same declarations. */
@supports not (container-type: inline-size) {
  @media (max-width: 640px) {
    .q-facts-overlay { top: auto; bottom: calc(32px + 0.4rem); left: 0.7rem; right: 0.7rem; width: auto; pointer-events: none; }
    .q-facts-overlay .q-fact { padding: 0; line-height: 1.4; }
  }
}
/* The foot (the orientation note) sits under the model at every width: margin-top: auto in its own rule above. It used to be set
   under 600 px only, so on a wide screen the foot sat at the top of the viewer behind the model and its note never showed
   (seen 2026-10-24, DFM checks step 1). */

/* --- one-line honest progress state: real ticks, no fake bar, no layer
   count. Active pulses (slicing in progress); done gets a check; pending
   stays dim. --- */
.q-progress-wrap { flex: none; }
.q-ticks { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.15rem 0.85rem;
  font-family: ui-monospace, monospace; font-size: 0.7rem; letter-spacing: 0.04em; }
.q-tick { display: flex; align-items: center; gap: 0.35rem; color: var(--muted); opacity: 0.55; transition: opacity 150ms ease, color 150ms ease; }
.q-tick-dot { flex: none; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--line); border: 1px solid var(--muted); }
.q-tick.is-done, .q-tick.is-active, .q-tick.is-error { opacity: 1; color: var(--ink); }
.q-tick.is-done .q-tick-dot { background: var(--yellow); border-color: var(--yellow); }
.q-tick.is-active .q-tick-dot { background: var(--yellow); border-color: var(--yellow); animation: qPulse 1.1s ease-in-out infinite; }
.q-tick.is-error { color: #8a1c1c; }
.q-tick.is-error .q-tick-dot { background: #c0506a; border-color: #c0506a; }
@keyframes qPulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(255,199,0,0.45); } 50% { box-shadow: 0 0 0 5px rgba(255,199,0,0); } }
@media (prefers-reduced-motion: reduce) { .q-tick.is-active .q-tick-dot { animation: none; } }

/* --- sticky mobile price bar (below 1100px, only while a quote exists;
   matches the .q-app stacked-layout breakpoint above). Its sides are the page's own gutter (--pad), so the price lines up with
   the page's left edge and Save part with its right (2026-10-26: clamp(1rem, 4vw, 1.5rem) put the price 1.6 px outside the
   page's edge on a phone and up to 20 px outside it on a tablet). --- */
.q-sticky-bar { display: none; }
@media (max-width: 1099px) {
  .q-sticky-bar:not([hidden]) { display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 150; background: var(--black);
    border-top: 3px solid var(--yellow); padding: 0.7rem var(--pad); }
  .q-sticky-price { display: flex; flex-direction: column; min-width: 0; }
  .q-sticky-total { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-weight: 700;
    font-size: 1.3rem; color: var(--yellow); }
  .q-sticky-bar .btn { margin: 0; }
  /* The bar is fixed to the viewport bottom, so anything that scrolls to
     that band renders underneath it -- reserve real space below the form
     grid (measured: bar is ~78px) so the last line of a long price
     breakdown (e.g. 6 parts) can always be scrolled clear of it. */
  body:has(.q-sticky-bar:not([hidden])) .q-instrument-compact { padding-bottom: 6rem; }
}

/* --- the steps (2026-10-09). One is on screen at a time ([hidden]); its heading takes the focus when it does. --- */
.q-step[hidden] { display: none; }
.q-step h2[tabindex="-1"]:focus:not(:focus-visible), .q-drop h2[tabindex="-1"]:focus:not(:focus-visible) { outline: none; }
/* Card 806: nor after a pointer or a drop (main.js noteInput()), when Chrome rings a heading the page focused before it had the focus
   itself (a file dropped in from another window, then the preview turned by touch). A key brings the ring back. */
:root[data-input="pointer"] .q-step h2[tabindex="-1"]:focus, :root[data-input="pointer"] .q-drop h2[tabindex="-1"]:focus { outline: none; }
/* A returning visitor (2026-10-25): while the parts of an earlier visit may be coming back, the inline script in get-a-quote.njk
   marks the page and the steps stay out of sight with their room kept, so the drop zone never shows for a split second first.
   main.js takes the mark off once it has opened the right step (showSteps()); the script's own timer does after 3 s. */
.q-resuming .q-step { visibility: hidden; }

/* Step 1: the drop zone and nothing else. */
.q-drop { max-width: 760px; margin: 1.5rem auto 0; border: 3px dashed var(--black); background: var(--panel);
  padding: clamp(1.75rem, 5vw, 3rem) 1.25rem; text-align: center; display: grid; gap: 0.9rem; justify-items: center; }
.q-drop h2 { margin: 0; font-size: clamp(1.3rem, 3.5vw, 1.7rem); }
.q-drop .hint { margin: 0; }
.q-drop.is-dragover { border-color: var(--yellow); background: var(--hover); }
.q-drop .q-upload-note { max-width: 32rem; }
.q-after-drop { max-width: 920px; margin: 1.25rem auto 0; display: grid; gap: 0.75rem; text-align: center; }
.q-after-drop .hint { margin: 0; }
.q-samples { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; justify-content: center; }
/* Order again (customer accounts phase 3, 2026-10-24): the sentence about a link from the account pages, above the drop zone. */
.q-again-note { max-width: 760px; margin: 1.5rem auto 0; text-align: center; background: var(--panel); }
.q-again-note:empty { display: none; }
.q-again-note + .q-drop { margin-top: 1rem; }
.q-again-note .q-link-btn, .q-again-missing .q-link-btn { font-size: inherit; }
/* Its links read as the page's link buttons beside them (.q-link-btn): one look for everything to click in these lines. */
.q-again-note a, .q-again-missing a { color: var(--heading); text-decoration: underline; text-decoration-color: var(--yellow);
  text-decoration-thickness: 2px; text-underline-offset: 3px; }
.q-again-note a:hover, .q-again-missing a:hover { color: var(--ink); }

/* Step 2: parts list | viewer | this part's settings and price. */
.q-cfg { display: grid; grid-template-columns: 240px minmax(0, 1fr) 400px; gap: 1.25rem; align-items: start; }
.q-parts { background: var(--panel); border: 1px solid var(--line); min-width: 0; }
.q-parts.is-dragover { outline: 2px dashed var(--yellow); outline-offset: -2px; }
.q-parts-head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--line); }
.q-parts-title { font-family: ui-monospace, monospace; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.q-parts-count { font-size: 0.8rem; color: var(--muted); }
.q-parts-list { list-style: none; margin: 0; padding: 0; }
.q-part { position: relative; border-bottom: 1px solid var(--line); }
.q-part-pick { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.15rem 0.6rem; width: 100%; padding: 0.7rem 2.2rem 0.7rem 0.75rem;
  background: var(--panel); border: 0; border-left: 4px solid transparent; text-align: left; font: inherit; color: var(--ink); cursor: pointer; }
.q-part-pick:hover { background: var(--hover); }
.q-part.is-active .q-part-pick { border-left-color: var(--yellow); background: var(--hover); }
.q-part-name { font-weight: 700; font-size: 0.9rem; min-width: 0; overflow-wrap: anywhere; }
.q-part-price { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-weight: 700; font-size: 0.85rem; white-space: nowrap; }
.q-part-sum { grid-column: 1 / -1; font-size: 0.76rem; color: var(--muted); overflow-wrap: anywhere; text-wrap: balance; }
.q-part-state { grid-column: 1 / -1; font-size: 0.72rem; color: var(--ink-soft); }
.q-part.is-saved .q-part-state::before { content: "\2713\00a0"; }
.q-part.is-custom .q-part-state, .q-part.is-changed .q-part-state { color: #8a1c1c; }
.q-part-remove { position: absolute; top: 0.45rem; right: 0.35rem; width: 1.6rem; height: 1.6rem; border: 0; background: none;
  font-size: 1.1rem; line-height: 1; color: var(--muted); cursor: pointer; }
.q-part-remove:hover { color: var(--ink); }
.q-parts-foot { padding: 0.75rem 0.9rem; display: grid; gap: 0.6rem; }
.q-parts-foot .q-btn-outline { width: 100%; }
/* Owner, 2026-10-24: "Add another file" is a box files can be dropped into too, like step 1's. The whole parts panel takes the drop
   (main.js); the box lights up with it. On a touch screen, where nothing is dragged, it is the button alone. */
.q-add-drop { width: 100%; display: flex; flex-direction: column; align-items: center; gap: 0.15rem; padding: 0.75rem;
  font: inherit; cursor: pointer; background: var(--panel); color: var(--ink); border: 2px dashed var(--line); }
.q-add-drop:hover, .q-add-drop:focus-visible { border-color: var(--ink); }
.q-parts.is-dragover .q-add-drop { border-color: var(--yellow); background: var(--hover); }
.q-add-drop-title { font-size: 0.9rem; font-weight: 600; }
.q-add-drop-hint { font-size: 0.78rem; color: var(--muted); }
@media (hover: none) { .q-add-drop-hint { display: none; } }
.q-parts-order .q-mini-card-p { margin-top: 0.2rem; }
/* The preview is an ordinary part of the page (owner, 2026-10-10, after it had been made sticky and then fixed to the screen):
   it stays where it sits in the layout and scrolls up behind the header with everything else. Nothing follows the screen. */
.q-step-configure .q-viewer { height: clamp(340px, 62vh, 620px); }
.q-step-configure .q-form-col { min-width: 0; }
.q-step-configure .q-config-card { padding: 1rem 1.1rem; }
.q-config-head h2 { overflow-wrap: anywhere; }
.q-step-configure #q-strength-field, .q-step-configure #q-layer-field { grid-column: 1 / -1; }
.q-label-row { justify-content: space-between; gap: 0.5rem; }
.q-saved-all { background: var(--panel); border: 1px solid var(--line); border-top: 3px solid var(--yellow); padding: 0.9rem 1.1rem;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.6rem; }
.q-saved-all[hidden] { display: none; }
.q-saved-all p { margin: 0; font-weight: 600; }
.q-saved-all .btn { margin: 0; }
.q-price-row #q-add:disabled, .q-price-row #q-add[aria-disabled="true"] { background: var(--line); color: var(--ink-soft); cursor: default; }
/* Two prices on step 2 (owner ask 2026-10-11, CONTRACT-bodies-prices-hardware.md section 2): the standard tier and the fast one side
   by side, each its label, price and ship day; the fast one adds one line saying what it adds. Dark text on white, the order's
   tier outlined in ink on the pale yellow ground (no yellow text on white). */
.q-tier-pick { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; margin-top: 0.6rem; }
.q-tier-opt { display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem; min-width: 0; padding: 0.6rem 0.75rem;
  font: inherit; text-align: left; color: var(--ink); background: var(--panel); border: 2px solid var(--line); cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease; }
.q-tier-opt:hover:not(.is-selected):not(:disabled):not([aria-disabled="true"]) { background: var(--hover); }
.q-tier-opt.is-selected { border-color: var(--ink); background: var(--hover); }
.q-tier-opt:disabled, .q-tier-opt[aria-disabled="true"] { cursor: not-allowed; opacity: 0.6; }
.q-tier-label { font-family: ui-monospace, monospace; font-size: 0.7rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.q-tier-opt.is-selected .q-tier-label { color: var(--ink); font-weight: 700; }
.q-tier-price { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-weight: 700; font-size: 1.45rem;
  line-height: 1.1; white-space: nowrap; }
/* Card 802: the month and the day are joined by a no-break space (text.js keepTogether()), and the two lines are balanced, so a ship
   line that wraps reads "Ships by / Wednesday, Oct 28", never "Ships by Wednesday, Oct / 28". */
.q-tier-ship { font-size: 0.78rem; line-height: 1.3; color: var(--ink-soft); text-wrap: balance; }
.q-tier-adds { font-size: 0.74rem; line-height: 1.3; color: var(--muted); }
@media (max-width: 380px) { .q-tier-price { font-size: 1.2rem; } }
.cs-charge small { font-family: "Noto Sans", sans-serif; font-size: 0.75rem; color: var(--muted); }

/* The pieces question (section 1): above the price, and above the lead form when "Several parts" sent the part there. */
.q-pieces { background: var(--panel); border: 1px solid var(--line); border-top: 3px solid var(--yellow); padding: 0.85rem 1.1rem; min-width: 0; }
.q-pieces-q { margin: 0 0 0.6rem; font-weight: 600; font-size: 0.95rem; color: var(--ink); }
.q-pieces-choices { display: grid; gap: 0.5rem; }
.q-pieces-btn { width: 100%; min-height: 44px; padding: 0.5rem 0.75rem; font: inherit; font-size: 0.88rem; line-height: 1.35; text-align: left;
  color: var(--ink); background: var(--panel); border: 2px solid var(--line); cursor: pointer; transition: background 150ms ease; }
.q-pieces-btn:hover:not([aria-pressed="true"]) { background: var(--hover); }
.q-pieces-btn[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--white); font-weight: 700; }
.q-form-col:has(> #quote-oversize:not([hidden])) > #q-pieces { order: -1; }

/* Hardware (section 3): plain check boxes in the Configure card, in the card's own type (not its small capitals labels). */
.q-config-grid .q-hw-boxes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.35rem 1rem; margin-top: 0.15rem; }
.q-config-grid .q-hw-box { display: flex; align-items: center; gap: 0.5rem; min-width: 0; font-family: inherit; font-size: 0.88rem;
  text-transform: none; letter-spacing: normal; color: var(--ink); cursor: pointer; }
.q-config-grid .q-hw-box input[type="checkbox"] { flex: none; width: 1.1rem; height: 1.1rem; margin: 0; padding: 0; accent-color: var(--ink); }
.q-config-grid .q-hw-notes { margin-top: 0.35rem; }
.q-config-grid .q-hw-notes input { height: 44px; padding-block: 0; }
@media (max-width: 380px) { .q-config-grid .q-hw-boxes { grid-template-columns: 1fr; } }
/* Owner ask 2026-10-11: one "Hardware" box opens the five and the notes, set in under it so they read as its options. */
.q-config-grid .q-hw-toggle { justify-self: start; width: max-content; max-width: 100%; margin-top: 0.15rem; font-weight: 600; }
.q-config-grid .q-hw-options { margin-top: 0.15rem; padding-left: 1.6rem; }
.q-config-grid .q-hw-options[hidden] { display: none; }
.q-step-nav { display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: space-between; align-items: center; margin-top: 1.25rem; }
.q-step-nav .btn { margin: 0; }
.q-step-nav .btn:disabled { opacity: 0.5; cursor: default; }
/* The site's outline button is yellow text, made for black grounds. On these white panels it is dark text with a dark
   outline (no yellow text on white), yellow only on hover. */
.q-step-nav .btn-secondary, .cart-sent .btn-secondary, .q-saved-all .btn-secondary { color: var(--ink); border-color: var(--ink); background: var(--panel); }
.q-step-nav .btn-secondary:not(:disabled):hover, .cart-sent .btn-secondary:hover { background: var(--yellow); color: var(--black); border-color: var(--yellow); }
/* Card 803 (2026-10-26): Back to upload (the quiet button) was in the body face at 0.95rem and 51 px tall beside Review order in the
   button face at 1.05rem and 53 px, and stacked on a phone at two widths. It takes the button's face, size and padding, its colours
   stay quiet, and once the two no longer fit side by side each takes the full width. */
.q-step-nav .btn-quiet { font-family: "Konkhmer Sleokchher", sans-serif; font-size: 1.05rem; line-height: 1; padding: 1rem 2rem; }
/* Side by side they take 189.5 + 12 + 176.4 = 378 px (measured); in a narrower row both take the whole width. */
.q-step-nav { container: q-step-nav / inline-size; }
@container q-step-nav (max-width: 380px) {
  .q-step-nav .btn, .q-step-nav .btn-quiet { flex: 1 1 100%; }
}
/* 2026-10-24: the same for every outline button on a white section or in a pop-up (the color picker's "Request a color instead",
   the Close buttons, /pay/thanks/ and /pay/cancelled/ were yellow text on white). :where() keeps it at the button's own weight, so
   the panels above that set their own look (the oversize panel, the yellow band) still win. */
:where(.section:not(.on-black), .sp-panel, .sp-detail-panel, .fi-panel, .acct-page) .btn-secondary { color: var(--ink); border-color: var(--ink); background: var(--panel); }
:where(.section:not(.on-black), .sp-panel, .sp-detail-panel, .fi-panel, .acct-page) .btn-secondary:where(:not(:disabled)):hover { background: var(--yellow); color: var(--black); border-color: var(--yellow); }
/* Step 2's settings column is narrow: the Color button shows the pick, not the word "Choose" beside it. */
.q-step-configure .q-color-choose { display: none; }
@media (max-width: 1199px) {
  .q-cfg { grid-template-columns: minmax(0, 1fr) 380px; }
  .q-parts { grid-column: 1 / -1; }
}
@media (max-width: 820px) {
  .q-cfg { grid-template-columns: minmax(0, 1fr); }
  .q-step-configure .q-hero { position: static; }
  .q-step-configure .q-viewer { height: 300px; }
  /* On a phone the parts list is a compact strip above the viewer. */
  .q-parts-list { display: flex; overflow-x: auto; }
  .q-part { flex: 0 0 min(220px, 70vw); border-bottom: 0; border-right: 1px solid var(--line); }
  .q-parts-foot { grid-template-columns: auto minmax(0, 1fr); align-items: center; }
  .q-parts-foot .q-btn-outline { width: auto; padding: 0 0.8rem; }
  .q-parts-foot .q-add-drop { width: auto; padding: 0.55rem 0.8rem; }
}

/* Steps 3 and 4: the order. */
.q-review, .q-checkout { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 1.5rem; align-items: start; }
.q-review-main h2, .q-checkout-main h2 { margin: 0 0 1rem; font-size: clamp(1.4rem, 2.6vw, 1.8rem); }
.cart-sent h2 { font-size: clamp(1.4rem, 2.6vw, 1.8rem); }
.q-order-table { background: var(--panel); border: 1px solid var(--line); }
.cart-table-head { display: grid; grid-template-columns: 48px minmax(0, 1fr) 7.5rem 6rem; gap: 0.75rem; padding: 0.55rem 0.9rem;
  border-bottom: 1px solid var(--line); font-family: ui-monospace, monospace; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.cart-table-head span:first-child { grid-column: 1 / 3; }
.cart-table-head span:last-child { text-align: right; }
.q-order-table .cart-lines { margin: 0; }
.q-order-table .cart-empty { padding: 1rem 0.9rem; margin: 0; }
.q-order-table .cart-line { display: grid; grid-template-columns: 48px minmax(0, 1fr) 7.5rem 6rem; gap: 0.75rem; align-items: start;
  padding: 0.8rem 0.9rem; border-top: 0; }
.q-order-table .cart-line:last-child { border-bottom: 0; }
.cl-actions { display: flex; gap: 0.9rem; margin-top: 0.35rem; }
.cl-edit { font: inherit; font-size: 0.82rem; background: none; border: 0; color: var(--ink); text-decoration: underline;
  text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; cursor: pointer; padding: 0; }
/* Round Q (2026-10-13): in place of Edit on a line the page can no longer change ("Upload the file again to change this part."). */
.cl-edit-locked { font-size: 0.82rem; color: var(--muted); }
/* Order again: the order's lines that could not be added, one sentence each, under the table of parts. */
.q-again-missing { list-style: none; margin: 1rem 0 0; display: grid; gap: 0.45rem; background: var(--panel); overflow-wrap: anywhere; }
.q-again-missing li { margin: 0; }
.cl-qty-cell { min-width: 0; }
.q-order-table .cl-right { align-items: flex-end; }
.cl-price { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-weight: 700; white-space: nowrap; }
.q-order-aside { background: var(--panel); border: 1px solid var(--line); border-top: 3px solid var(--yellow); padding: 1rem 1.1rem;
  display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; overflow-wrap: anywhere; }
/* Step 4 pins its order box on its own; step 3 pins the whole right-hand column (.q-review-side, below). */
.q-checkout .q-order-aside { position: sticky; top: calc(96px + 1rem); max-height: calc(100vh - 96px - 2rem); }
/* Step 3 (owner, 2026-10-14): the lead time, order box, checks and custom quote cards are one column beside the table of parts, pinned
   below the header, the lead time and the order box (Total, Continue to checkout) first.
   Owner, 2026-10-24 ("get rid of the scroll bar here"): no height of its own and no scroll inside it. Taller than the screen, its top
   stays pinned and its lower cards come into view as the page reaches its end. */
.q-review-side { display: flex; flex-direction: column; gap: 1rem; position: sticky; top: calc(96px + 1rem); min-width: 0; }
.q-review-side > * { flex: 0 0 auto; }
/* The order box here does not repeat the parts: the table beside it already lists each one with its quantity and price (owner, 2026-10-14:
   lead time, Total and the button must all fit on a laptop screen). The lead time row repeats the card above it,
   which shows the pick. Step 4 has no table and no card, so its box keeps both. */
.q-review .cs-lines, .q-review .cs-leadtime { display: none; }
.q-review-side .q-leadtime-card { margin-bottom: 0; }
.q-order-aside > * { flex: 0 0 auto; }
.q-order-aside .cart-summary { flex: 0 1 auto; min-height: 0; display: flex; flex-direction: column; }
.cart-summary .cs-lines { flex: 0 1 auto; min-height: 3.5rem; overflow-y: auto; overscroll-behavior: contain; }
.cart-summary > * { flex-shrink: 0; }
.cart-summary > .cs-lines { flex-shrink: 1; }
.q-order-aside h3 { margin: 0; font-size: 1.05rem; }
.q-order-aside-head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
.q-order-aside .cart-summary { margin: 0; }
.q-order-aside .hint { margin: 0; font-size: 0.8rem; }
.q-order-aside .q-link-btn { justify-self: start; align-self: flex-start; }
.cs-row.cs-line { color: var(--ink); font-size: 0.9rem; }
/* What the order adds after its parts (2026-10-11): the first such line is set off from the list of parts above it. */
.cs-row.cs-charge { gap: 0.75rem; }
.cs-row.cs-charge > span:last-child { text-align: right; }
.cs-row.cs-line + .cs-row.cs-charge { border-top: 1px solid var(--line); margin-top: 0.3rem; }
#cart-checkout .btn { margin: 0; }
#cart-checkout .btn-primary { flex: 1 1 100%; }
.q-checkout-main #cart-contact { margin-top: 0; border-top: 0; padding-top: 0; background: var(--panel); border: 1px solid var(--line); padding: 1.1rem 1.2rem; }
/* Card 287: everything the customer works in; the confirmation (.cart-sent) is its sibling and only one of the two shows. */
.cart-ui { display: block; }
.cart-sent { max-width: 640px; margin: 1.5rem auto 0; padding: 1.5rem 1.25rem; background: var(--panel); border: 1px solid var(--line); border-top: 3px solid var(--yellow); }
.cart-sent[hidden] { display: none; }
.cart-sent h2 { margin-top: 0; }
@media (max-width: 960px) {
  .q-review, .q-checkout { grid-template-columns: minmax(0, 1fr); }
  .q-review-side, .q-checkout .q-order-aside { position: static; max-height: none; overflow-y: visible; }
  .cart-summary .cs-lines { overflow-y: visible; }
}
@media (max-width: 640px) {
  .cart-table-head { display: none; }
  .q-order-table .cart-line { grid-template-columns: 48px minmax(0, 1fr); }
  .q-order-table .cl-qty-cell { grid-column: 2; }
  .q-order-table .cl-right { grid-column: 2; align-items: flex-start; }
}

/* color swatch picker */
.swatch-picker {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(14,14,16,0.55);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
}
.sp-panel {
  background: var(--panel); width: min(760px, 100%); max-height: 88vh;
  display: flex; flex-direction: column; border: 2px solid var(--black);
}
.sp-head { display: flex; align-items: center; justify-content: space-between; padding: 0.9rem 1rem; border-bottom: 1px solid var(--line); }
.sp-close { border: 0; background: none; font-size: 1.5rem; line-height: 1; cursor: pointer; color: var(--ink); }
.sp-controls { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; padding: 0.75rem 1rem; }
.sp-controls input[type="search"] { flex: 1; min-width: 12rem; font: inherit; padding: 0.5rem 0.7rem; border: 2px solid var(--line); }
.sp-check { display: flex; align-items: center; gap: 0.4rem; font-size: 0.9rem; white-space: nowrap; }
.sp-families { display: flex; flex-wrap: wrap; gap: 0.35rem; padding: 0 1rem 0.6rem; }
.sp-fam { font: inherit; font-size: 0.8rem; padding: 0.25rem 0.6rem; border: 2px solid var(--line); background: var(--panel); cursor: pointer; }
.sp-fam.is-on { background: var(--yellow); border-color: var(--yellow); font-weight: 600; }
.sp-grid { overflow-y: auto; padding: 0.5rem 1rem 1rem; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.6rem; }
.sp-sw { display: block; border: 2px solid var(--line); background: var(--panel); padding: 0 0 0.55rem; cursor: pointer; text-align: left; font: inherit; }
.sp-sw:hover { border-color: var(--yellow); }
/* banner = solid swatch colour full-width, with the printed-sample photo squared
   off on the right edge (mirrors filamentcolors.xyz .card-img-container) */
.sp-banner { display: block; position: relative; width: 100%; aspect-ratio: 288 / 89; background: var(--line); overflow: hidden; }
.sp-solid { position: absolute; inset: 0; }
.sp-photo { position: absolute; top: 0; right: 0; height: 100%; aspect-ratio: 1; object-fit: cover; object-position: -8px center; background: #fff; }
.sp-nm, .sp-mf { display: block; padding: 0 0.5rem; }
.sp-nm { padding-top: 0.4rem; font-size: 0.82rem; font-weight: 600; line-height: 1.2; }
.sp-mf { font-size: 0.72rem; color: var(--muted); line-height: 1.2; }
.sp-empty, .sp-more { grid-column: 1 / -1; font-size: 0.85rem; color: var(--muted); text-align: center; margin: 0.5rem 0; }
.sp-foot { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; border-top: 1px solid var(--line); }
.sp-attr { font-size: 0.78rem; color: var(--muted); }
.sp-request-row { display: flex; gap: 0.5rem; padding: 0 1rem 1rem; }
.sp-request-row input { flex: 1; font: inherit; padding: 0.5rem 0.7rem; border: 2px solid var(--line); }
@media (max-width: 560px) {
  .sp-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}

/* colour detail lightbox (shadowbox): opens over the picker on a swatch click */
.sp-detail { position: fixed; inset: 0; z-index: 300; background: rgba(14,14,16,0.7);
  display: flex; align-items: center; justify-content: center; padding: 1rem; }
.sp-detail-panel { position: relative; background: var(--panel); border: 2px solid var(--black);
  width: min(460px, 100%); max-height: 92vh; overflow-y: auto; padding: 1rem; }
.sp-detail-x { position: absolute; top: 0.35rem; right: 0.5rem; border: 0; background: none;
  font-size: 1.6rem; line-height: 1; color: var(--muted); cursor: pointer; padding: 0.1rem 0.35rem; }
.sp-detail-x:hover { color: var(--ink); }
.sp-detail-imgwrap { background: #fff; border: 1px solid var(--line); aspect-ratio: 4 / 3;
  display: flex; align-items: center; justify-content: center; }
.sp-detail-img { display: block; width: 100%; height: 100%; object-fit: contain;
  opacity: 0; transition: opacity 0.15s ease; }
.sp-detail-img.is-loaded { opacity: 1; }
.sp-detail-info { margin: 0.8rem 0 1rem; }
.sp-detail-name { display: block; font-weight: 700; font-size: 1.05rem; }
.sp-detail-sub { display: block; font-size: 0.85rem; color: var(--muted); margin-top: 0.15rem; }
.sp-detail-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; }

/* "i" info button next to a field label + the finish comparison modal */
.field-info { display: inline-flex; align-items: center; justify-content: center;
  width: 1.15rem; height: 1.15rem; margin-left: 0.35rem; padding: 0; vertical-align: middle;
  border: 1.5px solid var(--muted); border-radius: 50%; background: none; color: var(--muted);
  font: italic 700 0.72rem/1 Georgia, "Times New Roman", serif; cursor: pointer; }
.field-info:hover { border-color: var(--ink); color: var(--ink); }
.fi-modal { position: fixed; inset: 0; z-index: 300; background: rgba(14,14,16,0.7);
  display: flex; align-items: center; justify-content: center; padding: 1rem; }
.fi-panel { position: relative; background: var(--panel); border: 2px solid var(--black);
  width: min(580px, 100%); max-height: 92vh; overflow-y: auto; padding: 1.25rem; }
.fi-panel h3 { margin: 0 2rem 0.35rem 0; }
.fi-intro { margin: 0 0 0.75rem; font-size: 0.88rem; color: var(--ink-soft); }
.fi-list { list-style: none; margin: 0; padding: 0; }
.fi-list li { display: grid; grid-template-columns: 88px 1fr; gap: 0.9rem; align-items: center;
  padding: 0.7rem 0; border-bottom: 1px solid var(--line); }
.fi-list li:last-child { border-bottom: 0; }
.fi-thumb { width: 88px; aspect-ratio: 1; object-fit: cover; object-position: -6px center;
  background: #fff; border: 1px solid var(--line); display: block; }
.fi-thumb.fi-noimg { background: var(--line); }
.fi-name { display: block; font-weight: 700; font-size: 0.95rem; }
.fi-blurb { display: block; font-size: 0.85rem; color: var(--ink-soft); margin-top: 0.15rem; }
.fi-attr { font-size: 0.78rem; color: var(--muted); margin: 0.85rem 0 1rem; }
.fi-actions { display: flex; justify-content: flex-end; }
@media (max-width: 480px) {
  .fi-list li { grid-template-columns: 64px 1fr; gap: 0.7rem; }
  .fi-thumb { width: 64px; }
}
/* material guide: no thumbnail column, plus a small uppercase tier heading */
.fi-list--mat li { grid-template-columns: 1fr; padding: 0.55rem 0; }
.fi-list--mat .fi-group { border-bottom: 0; padding: 1rem 0 0.15rem; font-family: "Noto Sans", sans-serif;
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
.fi-list--mat .fi-group:first-child { padding-top: 0; }
.check-list { list-style: none; padding: 0; margin: 1.25rem 0; }
.check-list li { padding-left: 1.6rem; position: relative; margin-bottom: 0.6rem; font-size: 0.98rem; }
.check-list li::before { content: ""; position: absolute; left: 0; top: 0.4em; width: 0.7rem; height: 0.7rem; background: var(--yellow); }
.quote-card.alt .check-list li { color: #e6e6e6; }

/* TODO markers */
.todo { border-left: 4px solid var(--yellow); background: #fff9e0; padding: 0.9rem 1.1rem; margin: 1.25rem 0; font-size: 0.95rem; color: var(--ink); }

/* Material index list on /materials/ */
.mat-index { list-style: none; margin: 0 0 1.5rem; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem 0.6rem; }
.mat-index li { margin: 0; }
.mat-index a {
  display: inline-block;
  padding: 0.5rem 0.95rem;
  border: 2px solid var(--yellow);
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.18);
  transition: box-shadow 0.18s ease, transform 0.18s ease;
}
.mat-index a:hover,
.mat-index a:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 0 10px 1px rgba(255, 199, 0, 0.45);
}
.todo strong { font-family: "Konkhmer Sleokchher", sans-serif; font-weight: 400; }

/* CTA band */
.cta-band { background: var(--yellow); text-align: center; padding-block: clamp(2.5rem, 6vw, 3.5rem); }
.cta-band h2 { color: var(--black); margin-bottom: 1.25rem; }
.cta-band .btn { border-color: var(--black); color: var(--black); background: transparent; }
.cta-band .btn:hover { background: var(--black); color: var(--yellow); }
/* Voice sample 2026-10-20: a band with the home hero's pair (custom parts only today, cta_consultation in
   src/_includes/layouts/service.njk). The quote button is the filled one, as in the hero. The two are the same width:
   side by side, then one under the other on phones. */
.cta-band__actions { display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 0.75rem 1rem; }
.cta-band__actions .btn { margin: 0; }
@media (max-width: 600px) { .cta-band__actions { grid-auto-flow: row; } }
.cta-band .btn-primary { background: var(--black); color: var(--yellow); }
.cta-band .btn-primary:hover { background: transparent; color: var(--black); }

/* -------------------------------------------------------------------------- */
/* Footer                                                                    */
/* -------------------------------------------------------------------------- */
.site-footer { background: var(--black); color: #cfcfcf; padding-top: clamp(2.5rem, 6vw, 3.5rem); font-size: 0.95rem; }
.footer-grid { display: grid; grid-template-columns: 1.8fr 1fr 1fr 1.3fr; gap: 2rem; padding-bottom: 2.5rem; }
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
/* Phones (owner 2026-10-26: "the sections can be placed next to eachother instead of just one long list of links"): Explore and
   Resources side by side, the brand line above them and Contact below them across the full width, so the email address never
   sits in a narrow column. The links keep their size and spacing. The 1rem column gap keeps the longest link ("How pricing
   works", 132 px) on one line at 320 px; test/footer-phone-columns-2026-10-26.test.js. The trailing margins go so the gaps
   between the sections are even, and a copyright line that has to wrap (320 px) splits in two even halves. */
@media (max-width: 560px) {
  .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 1rem; }
  .footer-brand, .footer-contact { grid-column: 1 / -1; }
  .footer-brand p, .footer-area, .footer-col li:last-child { margin-bottom: 0; }
  .footer-legal p:first-child { text-wrap: balance; }
  .footer-area { text-wrap: pretty; }
}
.footer-brand .brand-mark { font-family: "Konkhmer Sleokchher", sans-serif; font-size: 1.2rem; color: var(--yellow); }
.footer-brand p { margin-top: 0.75rem; max-width: 42ch; }
.footer-col h2 { font-family: "Konkhmer Sleokchher", sans-serif; font-size: 1rem; color: var(--yellow); margin-bottom: 0.9rem; }
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col li { margin-bottom: 0.5rem; }
.footer-col a { color: #e4e4e4; text-decoration: none; }
.footer-col a:hover { color: var(--yellow); }
.footer-area { margin-top: 1rem; }
.footer-legal { border-top: 1px solid var(--line-dark); padding-block: 1.25rem; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem; }
.footer-legal a { color: #e4e4e4; text-decoration: none; }
.footer-legal p { margin: 0; }
/* The quote page's price bar is fixed to the bottom of the screen under 1100 px while a priced part is on screen (step 2), and at
   the end of the page it covered the copyright and Terms · Privacy (measured 2026-10-26 at 320 to 768 px: the bar 78.17 px tall,
   over the last 78 px of the footer). While it shows, the footer keeps that much more black band under the legal row. */
@media (max-width: 1099px) { body:has(.q-sticky-bar:not([hidden])) .site-footer { padding-bottom: 5rem; } }

/* Utilities */
.lede { font-size: 1.12rem; color: var(--ink-soft); }
.center { text-align: center; }
.mt-2 { margin-top: 2rem; }

/* -------------------------------------------------------------------------- */
/* Case studies                                                              */
/* -------------------------------------------------------------------------- */
.cs-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1.25rem;
  margin: 1.75rem 0 2.5rem;
}
.cs-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: var(--ink);
  border: 2px solid var(--line);
  background: var(--panel);
  transition: border-color 0.14s ease;
}
.cs-card:hover { border-color: var(--yellow); }
.cs-card img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; background: #f3f3f3; }
.cs-card-body { padding: 1rem 1.1rem 1.2rem; display: flex; flex-direction: column; gap: 0.35rem; }
.cs-card-client {
  font-family: "Noto Sans", sans-serif;
  font-size: 0.78rem;   /* was 0.72rem (11.52px) -- under the 12px floor */
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
}
.cs-card h2 { font-size: 1.15rem; margin: 0; color: var(--heading); }
.cs-card p { margin: 0.15rem 0 0; font-size: 0.95rem; color: var(--ink-soft); }
.cs-card-link {
  margin-top: 0.6rem;
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 0.9rem;
  color: var(--heading);
  border-bottom: 3px solid var(--yellow);
  padding-bottom: 2px;
  align-self: flex-start;
}
.work-grid-heading { margin: 2rem 0 1rem; }

/* Plain (non-link) content cards: industry pages' "What we make" /
   "Materials" / "Real work" grids. */
.info-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.25rem;
  margin: 1.75rem 0 2.5rem;
}
.info-card { border: 2px solid var(--line); background: var(--panel); padding: 1.1rem 1.2rem 1.3rem; }
.info-card h3 { margin: 0 0 0.4rem; font-size: 1.05rem; color: var(--heading); }
.info-card p { margin: 0; font-size: 0.95rem; color: var(--ink-soft); }
.info-card a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 2px; }

/* Small pill-link rows: homepage "Industries we serve", case-studies
   "Related industries". Same look as .mat-index on /materials/. */
.pill-links { list-style: none; margin: 1.5rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem 0.6rem; }
.pill-links li { margin: 0; }
.pill-links a {
  display: inline-block;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--line, #e0ddd2);
  background: #fff;
  color: var(--ink);
  text-decoration: none;
  font-size: 0.9rem;
}
.pill-links a:hover { border-color: var(--yellow); }

.case-study .cs-head { background: var(--black); text-align: center; padding-block: clamp(3rem, 8vw, 5rem); }
.case-study .cs-head h1 { color: var(--yellow); }
.case-study .cs-head p { color: #d8d8d8; max-width: 60ch; margin-inline: auto; font-size: 1.1rem; }
.cs-hero {
  width: 100%;
  max-height: 62vh;
  object-fit: cover;
  display: block;
  background: #f3f3f3;
}
.cs-body { padding-block: clamp(2.5rem, 6vw, 4.5rem); }
.cs-body .wrap { max-width: 860px; }
.cs-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem 2rem;
  margin-bottom: 2rem;
}
@media (max-width: 720px) { .cs-grid { grid-template-columns: 1fr; } }
.cs-grid h2 {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  font-family: "Noto Sans", sans-serif;
  font-weight: 700;
  margin-bottom: 0.4rem;
}
.cs-grid p { margin: 0; font-size: 1rem; }
.cs-narrative { font-size: 1.05rem; line-height: 1.7; margin: 1.5rem 0; }
.cs-narrative p { margin: 0 0 1rem; }
.cs-quote {
  margin: 2rem 0;
  padding: 1.25rem 1.5rem;
  border-left: 4px solid var(--yellow);
  background: var(--bluebg, #fff7dc);
}
.cs-quote p { margin: 0 0 0.5rem; font-size: 1.15rem; font-style: italic; }
.cs-quote cite { font-style: normal; font-size: 0.9rem; color: var(--muted); }
.cs-gallery { margin: 2rem 0 1rem; }
.cs-back { margin-top: 2rem; }

.cs-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  margin: -1px 0 2.5rem;
}
.cs-stat {
  background: var(--black);
  padding: 1.5rem 1rem;
  text-align: center;
}
.cs-stat .cs-stat-value {
  display: block;
  color: var(--yellow);
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: clamp(1.6rem, 4vw, 2.2rem);
  line-height: 1.1;
  font-weight: 700;
}
.cs-stat .cs-stat-label {
  display: block;
  margin-top: 0.4rem;
  color: #d8d8d8;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
@media (max-width: 480px) { .cs-stats { grid-template-columns: 1fr; } }

/* -------------------------------------------------------------------------- */
/* Case studies list (/case-studies/)                                         */
/* -------------------------------------------------------------------------- */
.cs-list { display: flex; flex-direction: column; gap: 2.5rem; }
.cs-item { border: 1px solid var(--line); padding: clamp(1.25rem, 3vw, 2rem); }
.cs-item-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 1.25rem;
  margin-bottom: 1.5rem;
}
.cs-item-head h2 { margin-bottom: 0.25rem; }
.cs-item-client { margin: 0; color: var(--muted); font-size: 0.95rem; }
.cs-badge {
  flex: 0 0 auto;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: var(--yellow);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.15;
}
.cs-badge .v { font-family: "Konkhmer Sleokchher", sans-serif; font-size: 1.3rem; color: var(--black); }
.cs-badge .l { margin-top: 0.15rem; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--black); max-width: 82px; line-height: 1.15; }
.cs-item-body {
  display: grid;
  grid-template-columns: minmax(280px, 38%) 1fr;
  gap: 1.75rem;
  align-items: start;
}
@media (max-width: 720px) { .cs-item-body { grid-template-columns: 1fr; } }
.cs-item-img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; background: #f3f3f3; }
.cs-item-table-wrap { overflow-x: auto; }
.cs-compare-table { width: 100%; border-collapse: collapse; min-width: 360px; }
.cs-compare-table th, .cs-compare-table td { padding: 0.6rem 0.9rem; border-bottom: 1px solid var(--line); text-align: left; font-size: 0.92rem; }
.cs-compare-table thead th {
  background: var(--black);
  color: #fff;
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-weight: 400;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.cs-compare-table thead th.col-after { background: var(--ink, #1a1a1a); }
.cs-compare-table tbody th { font-weight: 600; color: var(--heading); white-space: nowrap; }
.cs-compare-table td.col-after { background: #fff7dc; font-weight: 700; }
/* On a phone the comparison fits its card instead of scrolling sideways (2026-10-24: at 375 px the "3D printed" column was cut off). */
@media (max-width: 480px) {
  .cs-compare-table { min-width: 0; }
  .cs-compare-table th, .cs-compare-table td { padding: 0.5rem 0.45rem; font-size: 0.84rem; }
}

/* -------------------------------------------------------------------------- */
/* Lightbox                                                                    */
/* -------------------------------------------------------------------------- */
.lightbox-trigger { cursor: zoom-in; }
.lightbox-overlay {
  position: fixed;
  inset: 0;
  z-index: 999;
  background: rgba(0, 0, 0, 0.9);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 5vw, 3rem);
}
.lightbox-overlay[hidden] { display: none; }
.lightbox-img {
  max-width: 100%;
  max-height: 100%;
  display: block;
  box-shadow: 0 10px 50px rgba(0, 0, 0, 0.6);
}
.lightbox-close {
  position: absolute;
  top: clamp(0.75rem, 3vw, 1.5rem);
  right: clamp(0.75rem, 3vw, 1.5rem);
  background: none;
  border: none;
  color: #fff;
  font-size: 2.25rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.25rem 0.6rem;
}
.lightbox-close:hover, .lightbox-close:focus-visible { color: var(--yellow); }
body.lightbox-open { overflow: hidden; }

/* -------------------------------------------------------------------------- */
/* Materials hub                                                             */
/* -------------------------------------------------------------------------- */
.mat-group-head { max-width: 60ch; margin-bottom: 2rem; }
.mat-group-head h2 { margin-bottom: 0.5rem; }
.mat-group-head .btn { margin-top: 1.25rem; }
.section.on-black .mat-group-head p { color: #d8d8d8; }
/* A text link in a black section was dark on black (the turnaround page link, on /materials/ and wherever else one sits in a black band). */
.section.on-black p a:not(.btn), .section.on-black li a:not(.btn):not(.mat-card) { color: var(--yellow); text-decoration: underline; text-decoration-color: var(--yellow); text-underline-offset: 3px; }

.mat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.1rem;
}
.mat-card {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 1.4rem;
  border: 2px solid var(--line);
  background: var(--panel);
  text-decoration: none;
  color: var(--ink);
  transition: border-color 0.14s ease;
}
.mat-card:hover { border-color: var(--yellow); }
.section.on-black .mat-card { background: var(--charcoal2, #1a1a1c); border-color: var(--line-dark); }
.mat-card h2, .mat-card h3 { margin: 0; color: var(--heading); font-size: clamp(1.3rem, 2.6vw, 1.6rem); }
.section.on-black .mat-card h2, .section.on-black .mat-card h3 { color: #fff; }
.mat-card p { margin: 0; font-size: 0.95rem; color: var(--ink-soft); }
.section.on-black .mat-card p { color: #cfcfcf; }
.mat-card-link {
  margin-top: auto;
  padding-top: 0.75rem;
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 0.85rem;
  color: var(--heading);
  border-bottom: 3px solid var(--yellow);
  align-self: flex-start;
}
.section.on-black .mat-card-link { color: var(--yellow); }

/* -------------------------------------------------------------------------- */
/* Tool pages (selector iframes + machining table)                           */
/* -------------------------------------------------------------------------- */
.tool-backbar {
  background: var(--black);
  border-bottom: 1px solid var(--line-dark);
  position: sticky;
  top: 96px;                 /* under the site header -- header-inner's
                                 min-height is 96px at every width (the logo
                                 shrinks at <=900px but min-height holds the
                                 row open), so this offset does not change */
  z-index: 40;
}
.tool-backbar .wrap {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 48px;
}
.tool-back {
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-size: 0.95rem;
  color: var(--yellow);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  padding: 0.25rem 0;
}
.tool-back:hover { border-bottom-color: var(--yellow); }
.tool-title {
  margin: 0 0 0 auto;
  font-family: "Noto Sans", sans-serif;
  font-size: 0.75rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: #9a9a92;
}

.tool-frame-wrap {
  height: calc(100vh - 96px - 48px);   /* same 96px header height at every width */
  min-height: 600px;
  background: var(--bg);
}
.tool-frame-wrap iframe { width: 100%; height: 100%; border: 0; display: block; }
.tool-cta { padding-top: 1.5rem; padding-bottom: 2rem; }

/* machining table */
.mm-filters { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 1.5rem 0 1.25rem; }
.mm-chip {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.4rem 0.9rem;
  border: 2px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
}
.mm-chip:hover { border-color: var(--yellow); }
.mm-chip.is-on { background: var(--yellow); border-color: var(--yellow); color: var(--black); font-weight: 600; }

.mm-scroll { overflow-x: auto; border: 1px solid var(--line); }
.mm-table { border-collapse: collapse; width: 100%; font-size: 0.88rem; line-height: 1.4; }
.mm-table th, .mm-table td { padding: 0.4rem 0.65rem; border-bottom: 1px solid var(--line); text-align: left; white-space: nowrap; vertical-align: top; }
.mm-name { white-space: normal; min-width: 16ch; max-width: 26ch; font-weight: 600; }
.mm-uses { display: block; font-weight: 400; font-size: 0.8rem; color: var(--muted); margin-top: 0.15rem; }
.mm-table thead th {
  position: sticky;
  top: 0;
  background: var(--black);
  color: #fff;
  font-family: "Noto Sans", sans-serif;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
}
.mm-table th.num, .mm-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.mm-table th.sortable { cursor: pointer; user-select: none; }
.mm-table th.sortable:hover { background: #222; }
.mm-arrow { display: inline-block; width: 0.8em; color: var(--yellow); }
.mm-table th.sorted-asc .mm-arrow::after { content: "\2191"; }
.mm-table th.sorted-desc .mm-arrow::after { content: "\2193"; }
.mm-table tbody tr:nth-child(even) td { background: var(--bg); }
.mm-table tbody tr:hover td { background: var(--hover, #fff7dc); }

/* -------------------------------------------------------------------------- */
/* Testimonials                                                              */
/* -------------------------------------------------------------------------- */
.testimonials { padding-block: clamp(3rem, 7vw, 5rem); }
.testimonials__heading { text-align: center; margin-bottom: 2rem; }
.testimonials__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 1.25rem;
  align-items: stretch;
}

.testimonial-card {
  margin: 0;
  border: 2px solid var(--line);
  border-top: 4px solid var(--yellow);
  background: var(--panel);
  padding: clamp(1.25rem, 3vw, 1.75rem);
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
.testimonial-card__quote {
  margin: 0;
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--ink);
}
.testimonial-card__quote::before { content: "\201C"; }
.testimonial-card__quote::after { content: "\201D"; }
/* Stars + attribution move as one group, pinned to the card's bottom edge
   (margin-top: auto on the shared wrapper, not on figcaption alone) so a
   shorter excerpt doesn't leave the star row floating mid-card while only
   the name drops to the bottom -- with the grid stretching every card to
   the row's tallest (align-items: stretch above), this is what keeps the
   stars/name/source line aligned across a row of uneven excerpt lengths. */
.testimonial-card__foot { display: flex; flex-direction: column; gap: 0.6rem; margin-top: auto; }
.testimonial-card__stars { display: flex; gap: 0.1rem; font-size: 0.95rem; line-height: 1; }
.testimonial-card__stars .star { color: var(--line); }
.testimonial-card__stars .star.is-on { color: var(--yellow); }
.testimonial-card__by {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.testimonial-card__name {
  font-family: "Konkhmer Sleokchher", sans-serif;
  font-style: normal;
  font-size: 1rem;
  color: var(--heading);
}
.testimonial-card__meta { font-size: 0.9rem; color: var(--muted); }
.testimonial-card__source { font-size: 0.85rem; color: var(--muted); }
.testimonial-card__source a { color: inherit; text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 2px; }
.testimonial-card__source a:hover { color: var(--ink); }

/* Home page reviews proof line: rating + customer count, slotted into
   testimonials.njk directly under its heading (via the proofLine var),
   and the "Read all reviews" link slotted in after the card grid (via
   afterCards) so the section reads top to bottom as heading -> proof line
   -> cards -> link. Dark-on-light by rule (no yellow text on white) --
   the per-card star color above is a separate, pre-existing pattern, not
   touched here. Tighten the heading's own margin only when a proof line
   actually follows it, so every other page using this heading (service/
   location/work, none of which set proofLine) is unaffected. */
.testimonials__heading:has(+ .reviews-proof) { margin-bottom: 0.6rem; }
.reviews-proof { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 0.6rem 1.5rem; margin-bottom: 2rem; }
.reviews-proof__rating { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 0.6rem; }
.reviews-proof__ratingnum { font-family: "Konkhmer Sleokchher", sans-serif; font-size: clamp(1.9rem, 3.2vw, 2.6rem); line-height: 1; color: var(--ink); }
.reviews-proof__stars { display: inline-flex; align-items: center; gap: 0.2rem; }
.reviews-proof__star { width: 28px; height: 28px; flex: none; }
.reviews-proof__star-outline { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linejoin: round; }
.reviews-proof__star-fill { fill: var(--yellow); stroke: var(--ink); stroke-width: 1.5; stroke-linejoin: round; }
.reviews-proof__sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.reviews-proof__ratingtext { font-size: 1rem; color: var(--ink); }
.reviews-proof__rule { width: 1px; height: 2rem; background: var(--line); flex: none; }
/* margin: 0 matters here, not just tidiness: this is a <p>, and the site's
   default paragraph bottom margin (1.1rem) was counting toward this flex
   item's cross-axis box height, so align-items: center centered a box
   taller than its own text and left the text sitting ~8.8px above the
   row's shared centerline instead of on it. */
.reviews-proof__customers { margin: 0; font-weight: 600; color: var(--ink); }
.reviews-proof__link { margin-top: 1.5rem; text-align: center; font-size: 0.9rem; }
@media (max-width: 600px) {
  .reviews-proof { flex-direction: column; gap: 0.5rem; }
  .reviews-proof__ratingtext { flex-basis: 100%; text-align: center; }
  .reviews-proof__rule { display: none; }
}

/* Placed inside a .prose column (service, work, location pages): the partial's
   own .wrap must not re-pad or re-cap the width. */
.prose .testimonials { padding-block: 0; margin-top: 3rem; }
.prose .testimonials > .wrap { padding-inline: 0; max-width: none; }

/* Dark bands (homepage) */
.on-black .testimonials__heading { color: var(--yellow); }
.on-black .testimonial-card {
  background: var(--charcoal2);
  border-color: var(--line-dark);
  border-top-color: var(--yellow);
}
.on-black .testimonial-card__quote { color: #eaeaea; }
.on-black .testimonial-card__name { color: #fff; }
.on-black .testimonial-card__meta { color: #b8b8b8; }
.on-black .testimonial-card__source { color: #9a9a94; }
.on-black .testimonial-card__source a { color: inherit; text-decoration-color: var(--line-dark); }
.on-black .testimonial-card__stars .star { color: #444; }
.on-black .testimonial-card__stars .star.is-on { color: var(--yellow); }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* The FDM cards on /materials/ jump to these tier headings. */
h3[id^="fdm-"] { scroll-margin-top: 1.5rem; }
/* The tier headings follow the section intro and button (the four cards are gone): one even gap above each. */
#fdm .mat-group-head { margin-bottom: 0; }
#fdm h3[id^="fdm-"] { margin: 2rem 0 0.75rem; }
/* The Materials menu lands on a section of /materials/: leave room for the sticky header. */
.section[id] { scroll-margin-top: 90px; }

/* Owner 2026-10-19: /work/ shows each photo whole at its own shape, in columns like a pinboard (no 4:3 crop).
   aspect-ratio "auto 4 / 3" holds the space at 4:3 until the photo loads, then takes the photo's own shape. */
.gallery.gallery--whole { display: block; columns: 4 240px; column-gap: 1rem; }
.gallery--whole figure { margin: 0 0 1rem; break-inside: avoid; }
.gallery--whole img { aspect-ratio: auto 4 / 3; object-fit: fill; }

/* Owner 2026-10-19: the home page's client band, the actual logos in colour with no label above them, matched to
   i-solids.com as measured: a white band with 72 px above and below the row, boxes 172 by 110 px with 36 px
   between them. The band keeps at least 48 px of side gutter at every width. Seven in one row from 1516 px
   (7 x 172 + 6 x 36 = 1420, plus the gutters), seven at 160 wide with 28 px gaps from 1384 px (7 x 160 + 6 x 28 = 1288),
   rows of four and three below that, two to a row (44% wide) on phones. Each logo is scaled to fill a
   160 by 84 px area at its own shape (object-fit: contain): square marks reach 84 px tall, wordmarks 160 px wide.
   As each company made it: no filter, full opacity, no hover, no borders. */
.section.client-band { background: #fff; padding-block: 72px; }
.client-band .wrap { max-width: none; padding-inline: 48px; }
.client-strip__list {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 36px;
  max-width: calc(4 * 172px + 3 * 36px);
}
.client-strip__item {
  box-sizing: border-box;
  flex: 0 0 172px;
  height: 110px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "HeadingPunct", "Konkhmer Sleokchher", system-ui, sans-serif;
  font-size: 1rem;
  line-height: 1.25;
  color: var(--muted);
  text-align: center;
}
.client-strip__item img { display: block; width: min(160px, 100%); height: 84px; object-fit: contain; }
@media (min-width: 1384px) {
  .client-strip__list {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 160px));
    column-gap: 28px;
    max-width: calc(7 * 160px + 6 * 28px);
  }
}
@media (min-width: 1516px) {
  .client-strip__list {
    grid-template-columns: repeat(7, minmax(0, 172px));
    column-gap: 36px;
    max-width: calc(7 * 172px + 6 * 36px);
  }
}
@media (max-width: 920px) {
  .client-strip__list { gap: 24px 12px; max-width: calc(4 * 172px + 3 * 12px); }
  .client-strip__item { flex: 0 1 calc((100% - 3 * 12px) / 4); }
}
@media (max-width: 640px) {
  .section.client-band { padding-block: 48px; }
  .client-strip__list { gap: 16px 6%; max-width: none; }
  .client-strip__item { flex: 0 0 44%; height: 90px; }
  .client-strip__item img { width: min(120px, 100%); height: 63px; }
}

/* -------------------------------------------------------------------------- */
/* Account pages (owner 2026-10-24: "go ahead with the account page design"; intuitive first, then clean). The quote tool    */
/* serves them under /account/ with this site's head, header and footer, in the markup of deploy/ACCOUNT-PAGES-MARKUP.md; the  */
/* staging preview at /account-preview/ shows them on sample data. The account header (Header, above), a breadcrumb and the   */
/* page's title, a light page, white cards: each order a row with a picture of the part, a plain-word status, one written-out  */
/* action and the rest in a menu; settings as cards of text with Edit (2026-10-25, Xometry's structure).                      */
/* -------------------------------------------------------------------------- */
.acct-vh { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* Retired 2026-10-25 (the account header does its job): the title bar, "Your account", the tabs, the quote button. These rules go
   once the quote tool's pages wear the account header. */
.acct-bar { background: var(--black); border-bottom: 4px solid var(--yellow); }
.acct-bar-inner { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 2.25rem; min-height: 64px; padding-block: 0.6rem; }
.acct-title { margin: 0; flex: none; font-family: "Konkhmer Sleokchher", sans-serif; font-size: 1.3rem; line-height: 1.2; color: var(--yellow); }
.acct-tabs { display: flex; align-items: center; gap: 1.75rem; min-width: 0; }
.acct-tab { display: inline-flex; align-items: center; gap: 0.45rem; padding: 0.35rem 0; font-family: "Konkhmer Sleokchher", sans-serif; font-size: 1rem;
  color: #cfcfcf; text-decoration: none; white-space: nowrap; border-bottom: 2px solid transparent; }
.acct-tab:hover { color: var(--white); }
.acct-tab[aria-current="page"] { color: var(--yellow); border-bottom-color: var(--yellow); }
.acct-tab-count { font-family: "Noto Sans", sans-serif; font-size: 0.74rem; font-weight: 700; line-height: 1.35rem; min-width: 1.35rem; padding: 0 0.4rem;
  text-align: center; border-radius: 999px; background: rgba(255, 255, 255, 0.14); color: #e6e6e6; }
.acct-tab[aria-current="page"] .acct-tab-count { background: var(--yellow); color: var(--black); }
/* The quote button: the header carries it from 1000 px up, so the bar shows its own only where the header's sits in the menu. */
.acct-bar-cta { display: none; margin-left: auto; align-items: center; height: 40px; padding: 0 1.1rem; font-size: 0.95rem; }
@media (max-width: 999px) { .acct-bar-cta { display: inline-flex; } }
@media (max-width: 760px) {
  .acct-bar-inner { gap: 0.5rem 1rem; padding-block: 0.7rem 0.4rem; }
  .acct-title { font-size: 1.15rem; }
  .acct-bar-cta { height: 36px; padding: 0 0.8rem; font-size: 0.85rem; }
  .acct-tabs { order: 3; flex-basis: 100%; gap: 1.4rem; overflow-x: auto; }
}

/* The page under it: light, with one white card. */
.acct-page { background: #f4f4f1; padding-block: clamp(1.25rem, 3vw, 2rem) clamp(2.5rem, 6vw, 4rem); }

/* The top of every signed-in page (Xometry's "Dashboard / Orders"): the breadcrumb in the site's own small capitals (as on a light
   page head), then the page's title. */
.acct-crumbs { margin: 0 0 0.35rem; }
.acct-crumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: 0.15rem 0.5rem; margin: 0; padding: 0; list-style: none;
  font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.12em; }
.acct-crumbs li { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.acct-crumbs li + li::before { content: "/"; color: #b3b0a6; font-weight: 400; }
.acct-crumbs a { color: var(--ink-soft); text-decoration: none; border-bottom: 2px solid transparent; }
.acct-crumbs a:hover { color: var(--ink); border-bottom-color: var(--yellow); }
.acct-crumbs [aria-current="page"] { color: var(--heading); overflow-wrap: anywhere; }
.acct-page-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem 1.25rem; margin: 0 0 1.25rem; }
.acct-page-title { margin: 0; font-size: clamp(1.7rem, 3.5vw, 2.2rem); }
.acct-card { position: relative; background: var(--panel); border: 1px solid #e6e6e2;
  --acct-cols: 48px minmax(9rem, 1fr) 6.25rem 6.25rem 10.5rem 5.5rem 13.25rem; --acct-gap: 1rem; }
/* Parts (phase 3): the part, when it was last ordered, how often, and Order again; no status and no total. */
.acct-card--parts { --acct-cols: 48px minmax(9rem, 1fr) 8rem 8.5rem 13.25rem; }

/* The toolbar: search, the date filter, the count (and the sort menu where the column heads are gone). */
.acct-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.75rem; padding: 1rem 1.25rem; border-bottom: 1px solid var(--line); }
.acct-search { position: relative; flex: 1 1 20rem; max-width: 27rem; }
.acct-search .acct-ico { position: absolute; left: 0.75rem; top: 50%; width: 16px; height: 16px; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.acct-search input { width: 100%; height: 40px; padding: 0 0.8rem 0 2.3rem; font: inherit; font-size: 0.95rem; color: var(--ink); background: var(--white);
  border: 1.5px solid var(--line); }
.acct-search input::placeholder { color: #8a8a83; }
.acct-select select { height: 40px; padding: 0 0.6rem; font: inherit; font-size: 0.92rem; color: var(--ink); background-color: var(--white); border: 1.5px solid var(--line); cursor: pointer; }
.acct-sort-select { display: none; }
.acct-count { margin: 0 0 0 auto; font-size: 0.85rem; color: var(--muted); white-space: nowrap; }

/* Column heads: the quote page's small capitals, each one a sort button. */
.acct-head { display: grid; grid-template-columns: var(--acct-cols); column-gap: var(--acct-gap); align-items: center; padding: 0.5rem 1.25rem;
  background: #fafaf8; border-bottom: 1px solid var(--line); font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.acct-sort { justify-self: start; display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.25rem 0; font: inherit; letter-spacing: inherit;
  text-transform: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
.acct-sort:hover, .acct-sort.is-sorted { color: var(--ink); }
/* The Total head sits flush right over the figures, its arrow on the left. */
.acct-sort--num { justify-self: end; flex-direction: row-reverse; }
.acct-sort-arrow { display: inline-block; width: 0.7em; text-align: center; }
.acct-sort[data-dir="asc"] .acct-sort-arrow::after { content: "\2191"; }
.acct-sort[data-dir="desc"] .acct-sort-arrow::after { content: "\2193"; }

/* The rows. */
.acct-rows { list-style: none; margin: 0; padding: 0; }
.acct-row:not([hidden]) ~ .acct-row { border-top: 1px solid #ecece8; }
.acct-row-main { display: grid; grid-template-columns: var(--acct-cols); column-gap: var(--acct-gap); align-items: center; min-height: 76px; padding: 0.75rem 1.25rem; }
.acct-cell { min-width: 0; }
.acct-k { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }
.acct-ref-link { font-weight: 700; font-size: 0.98rem; color: var(--ink); text-decoration: none; }
.acct-ref-link:hover { text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; }
.acct-sub { display: block; margin-top: 0.1rem; font-size: 0.82rem; font-weight: 400; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.acct-date, .acct-ship, .acct-times { font-size: 0.88rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; white-space: nowrap; }
.acct-muted { color: var(--muted); }
.acct-ship-card { display: none; }
.acct-total { text-align: right; font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; font-variant-numeric: tabular-nums; font-weight: 700;
  font-size: 0.92rem; color: var(--ink); white-space: nowrap; }
.acct-total .acct-muted { font-family: "Noto Sans", sans-serif; font-weight: 400; }
/* Fixed slots (the action, the menu, the chevron) so every row's button lines up, with or without a parts list or the script. */
.acct-actions { display: grid; grid-template-columns: auto 34px 34px; column-gap: 6px; align-items: center; justify-content: end; }
.acct-actions > .acct-btn { grid-column: 1; }
.acct-actions > .acct-menu { grid-column: 2; }
.acct-actions > .acct-expand { grid-column: 3; }

/* The picture of the part: the quote tool's drawing (gray on transparent) on a light tile, "+N" when the order has more. */
.acct-pic { position: relative; flex: none; display: block; width: 48px; height: 48px; border-radius: var(--radius); background: #f0f0ee; }
.acct-pic img { display: block; width: 100%; height: 100%; object-fit: contain; border-radius: inherit; }
.acct-pic-none { display: block; width: 100%; height: 100%; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b5b5b0' stroke-width='1.5'%3E%3Cpath d='M12 3l8 4.5v9L12 21l-8-4.5v-9z'/%3E%3Cpath d='M12 12l8-4.5M12 12v9M12 12L4 7.5'/%3E%3C/svg%3E") center / 22px no-repeat; }
.acct-pic-more { position: absolute; right: -6px; bottom: -6px; min-width: 1.4rem; padding: 0 0.3rem; line-height: 1.15rem; text-align: center;
  font-size: 0.68rem; font-weight: 700; color: var(--white); background: var(--ink); border: 2px solid var(--white); border-radius: 999px; }

/* Status: a word and a small icon on a tinted pill. Yellow is the one that needs the customer (Ready to pay). */
.acct-ico { flex: none; width: 12px; height: 12px; }
.acct-pill { display: inline-flex; align-items: center; gap: 0.32rem; padding: 0.24rem 0.65rem 0.24rem 0.5rem; border-radius: 999px; font-size: 0.78rem;
  font-weight: 600; line-height: 1.2; white-space: nowrap; }
.acct-pill--gray { background: #efefeb; color: #45453f; }
.acct-pill--amber { background: #fcefc7; color: #5f4500; }
.acct-pill--green { background: #e3f3e8; color: #1d5a36; }
.acct-pill--attn { background: var(--yellow); color: var(--black); font-weight: 700; }

/* The one written-out action: yellow to pay, black to order again, an outline for the rest. */
.acct-btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem; height: 34px; min-width: 8.25rem; padding: 0 0.85rem;
  font: inherit; font-size: 0.86rem; font-weight: 600; line-height: 1; white-space: nowrap; text-decoration: none; cursor: pointer;
  border: 1.5px solid #cfcfca; background: var(--white); color: var(--ink); transition: background 150ms ease, border-color 150ms ease, color 150ms ease; }
.acct-btn:hover { border-color: var(--ink); }
.acct-btn .acct-ico { width: 14px; height: 14px; }
.acct-btn--primary { background: var(--yellow); border-color: var(--yellow); color: var(--black); }
.acct-btn--primary:hover { background: var(--yellow-hi); border-color: var(--yellow-hi); }
.acct-btn--dark { background: var(--ink); border-color: var(--ink); color: var(--white); }
.acct-btn--dark:hover { background: var(--black); border-color: var(--black); color: var(--yellow); }
.acct-icon-btn { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 34px; height: 34px; padding: 0;
  background: var(--white); border: 1.5px solid #e2e2de; color: var(--ink-soft); cursor: pointer; transition: border-color 150ms ease, color 150ms ease; }
.acct-icon-btn:hover, .acct-icon-btn[aria-expanded="true"] { border-color: var(--ink); color: var(--ink); }
.acct-icon-btn .acct-ico { width: 16px; height: 16px; }
.acct-expand .acct-ico { transition: transform 150ms ease; }
.acct-expand[aria-expanded="true"] .acct-ico { transform: rotate(180deg); }

/* The row's menu. */
.acct-menu { position: relative; }
.acct-menu-list { position: absolute; right: 0; top: calc(100% + 6px); z-index: 20; min-width: 13rem; padding: 0.35rem 0; background: var(--white);
  border: 1px solid #e6e6e2; }
.acct-menu-list a { display: block; padding: 0.5rem 0.95rem; font-size: 0.9rem; color: var(--ink); text-decoration: none; white-space: nowrap; }
.acct-menu-list a:hover, .acct-menu-list a:focus-visible { background: #f4f4f1; outline-offset: -3px; }
/* A menu item that changes something (Remove, on a saved quote) is a form with its token; its button reads like the links. */
.acct-menu-form { margin: 0; }
.acct-menu-form button { display: block; width: 100%; padding: 0.5rem 0.95rem; border: 0; border-radius: 0; background: none; font: inherit;
  font-size: 0.9rem; color: var(--ink); text-align: left; white-space: nowrap; cursor: pointer; box-shadow: none; }
.acct-menu-form button:hover, .acct-menu-form button:focus-visible { background: #f4f4f1; outline-offset: -3px; }

/* A row's parts, opened in place. */
.acct-parts { list-style: none; margin: 0; padding: 0.25rem 1.25rem 0.7rem calc(1.25rem + 48px + var(--acct-gap)); background: #fafaf8; border-top: 1px solid #ecece8; }
.acct-part { display: grid; grid-template-columns: 36px minmax(0, 1fr); column-gap: 0.75rem; align-items: center; padding: 0.45rem 0; }
.acct-part + .acct-part { border-top: 1px solid #efefeb; }
.acct-part .acct-pic { width: 36px; height: 36px; border-radius: 6px; }
.acct-part-name { min-width: 0; font-size: 0.9rem; font-weight: 600; overflow-wrap: anywhere; }
.acct-part-name .acct-sub { white-space: normal; }

/* Nothing matches the search. */
.acct-none { padding: 2.25rem 1.25rem; text-align: center; color: var(--ink-soft); }
.acct-none p { margin: 0 0 0.9rem; }

/* Narrower screens: each row becomes a card (the picture, the number, the status, the ship date, then the total and the action). */
@media (max-width: 1079px) {
  .acct-head { display: none; }
  .acct-sort-select { display: block; }
  .acct-row-main { grid-template-columns: 48px minmax(0, 1fr) auto; grid-template-areas: "pic ref ref" "pic status status" "pic ship ship" "total total actions";
    column-gap: 0.9rem; row-gap: 0.3rem; align-items: start; padding: 1rem; }
  .acct-row-main > .acct-pic { grid-area: pic; }
  .acct-ref { grid-area: ref; }
  .acct-date { display: none; }
  .acct-ship { grid-area: ship; }
  .acct-ship .acct-k { position: static; width: auto; height: auto; overflow: visible; clip: auto; }
  .acct-ship-col { display: none; }
  .acct-ship-card { display: inline; }
  .acct-status { grid-area: status; }
  .acct-total { grid-area: total; align-self: center; text-align: left; font-size: 1rem; margin-top: 0.45rem; }
  .acct-actions { grid-area: actions; margin-top: 0.45rem; }
  .acct-parts { padding: 0.25rem 1rem 0.7rem calc(1rem + 48px + 0.9rem); }
  /* A part's card: its name, when it was last ordered and how often beside the picture, then Order again on a row of its own
     (beside it, "Ordered 2 times" had no room on a phone). */
  .acct-card--parts .acct-row-main { grid-template-areas: "pic ref ref" "pic date date" "pic times times" "actions actions actions"; }
  .acct-card--parts .acct-date { display: block; grid-area: date; }
  .acct-card--parts .acct-date .acct-k, .acct-card--parts .acct-times .acct-k { position: static; width: auto; height: auto; overflow: visible; clip: auto; }
  .acct-times { grid-area: times; }
}
/* Four tabs (Orders, Quotes, Parts, Settings) fit a phone's width without scrolling Settings off the edge. */
@media (max-width: 420px) {
  .acct-tabs { gap: 0.9rem; }
  .acct-tab { font-size: 0.92rem; }
}
@media (max-width: 360px) {
  .acct-tabs { gap: 0.6rem; }
  .acct-tab { gap: 0.3rem; font-size: 0.84rem; }
  .acct-tab-count { min-width: 1.15rem; padding: 0 0.3rem; line-height: 1.15rem; }
}
@media (max-width: 560px) {
  .acct-tools { padding: 0.9rem 1rem; }
  .acct-search { flex-basis: 100%; max-width: none; }
  .acct-select { flex: 1 1 0; }
  .acct-select select { width: 100%; }
  .acct-count { flex-basis: 100%; margin: 0; }
  .acct-btn { min-width: 0; }
  .acct-parts { padding-left: 1rem; }
}

/* Settings: one narrow card, on the same left edge as the title and the lists. */
.acct-card--narrow { max-width: 46rem; }
.acct-card--pad { padding: clamp(1.25rem, 3vw, 2rem); }
.acct-h2 { margin: 0 0 1rem; font-size: 1.2rem; color: var(--heading); }
.acct-label { font-weight: 700; font-size: 0.95rem; }
.acct-static { margin: 0; padding: 0.15rem 0 0.25rem; font-size: 1rem; }
.acct-section { margin-top: 1.75rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }
.acct-form .btn, .acct-set > .btn { padding: 0.8rem 1.5rem; font-size: 1rem; }
.acct-signout { display: flex; }
/* account.js brings the piece a sentence belongs to into view after a post (its form, the password block, its card), and an Edit
   link's #details, #password or #addresses lands on one: clear of the sticky header (96px). The sentence takes focus for screen
   readers only; it is not a control, so no focus ring. */
.acct-page form, .acct-page .acct-card, .acct-page .acct-set-sub { scroll-margin-top: calc(96px + 1rem); }
.acct-page .form-status:focus { outline: none; }

/* Account settings (2026-10-25, Xometry's /user-settings): a column of cards, as wide as the old settings card, on the lists' left
   edge. Details are text, a small label over each value, with Edit at the card's top right; the card being edited is a form (the
   quote tool draws it for ?edit=), Save then Cancel. A sentence after a post sits under the card's head. */
.acct-narrow > .acct-card { max-width: 46rem; }
.acct-set + .acct-set { margin-top: 1.25rem; }
.acct-set-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem 1rem; margin: 0 0 1rem; }
.acct-set-head .acct-h2 { margin: 0; }
.acct-set > .form-status { margin-top: 0; }
.acct-set-edit, .acct-set-link { font-size: 0.92rem; font-weight: 600; color: var(--ink); white-space: nowrap; text-decoration: underline;
  text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; }
.acct-set-edit:hover, .acct-set-link:hover { text-decoration-color: var(--ink); }
.acct-dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem 1.5rem; margin: 0; }
.acct-dl > div { min-width: 0; }
.acct-dl dt { font-size: 0.8rem; font-weight: 600; color: var(--muted); }
/* An empty value keeps its line, so the labels stay in step. */
.acct-dl dd { margin: 0.1rem 0 0; min-height: 1.6em; font-size: 1rem; color: var(--ink); overflow-wrap: anywhere; }
.acct-verified { display: inline-flex; align-items: center; gap: 0.25rem; margin-left: 0.35rem; padding: 0.1rem 0.5rem 0.1rem 0.4rem; border-radius: 999px;
  background: #e3f3e8; color: #1d5a36; font-size: 0.74rem; font-weight: 700; line-height: 1.35; vertical-align: 0.1em; white-space: nowrap; }
.acct-verified::before { content: ""; flex: none; width: 10px; height: 10px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.5l3 3 7-7.5' fill='none' stroke='%231d5a36' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
/* The password, in the details card under a line: its heading with Change password at the right (a sentence after a post goes
   under them), or its form under the heading. */
.acct-set-sub { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; column-gap: 1rem; margin-top: 1.5rem;
  padding-top: 1.25rem; border-top: 1px solid var(--line); }
.acct-set-sub > .acct-set-link { grid-column: 2; grid-row: 1; }
.acct-set-sub > .acct-form, .acct-set-sub > .form-status { grid-column: 1 / -1; }
.acct-set-sub > .acct-form { margin-top: 1rem; }
.acct-set-sub > .form-status { margin: 0.75rem 0 0; }
.acct-h3 { margin: 0; font-size: 1.1rem; color: var(--heading); }
.acct-set-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; }
.acct-set-actions .btn, .acct-set-actions .btn-quiet, .acct-address-remove .btn-quiet { display: inline-flex; align-items: center; justify-content: center;
  min-height: 46px; line-height: 1; text-decoration: none; }
.acct-set-actions .btn-quiet, .acct-address-remove .btn-quiet { padding: 0 1.2rem; }
.acct-check { display: flex; align-items: center; gap: 0.6rem; margin: 0.1rem 0 1.25rem; font-size: 0.95rem; cursor: pointer; }
.acct-check input { flex: none; width: 18px; height: 18px; margin: 0; accent-color: var(--yellow); }
/* Shipping addresses: a card each, two to a row; the default one tagged. One being edited takes the whole row, its Remove at the
   right of Save and Cancel (under them on a phone). */
.acct-addresses { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; margin: 0 0 1.25rem; padding: 0; list-style: none; }
.acct-address { position: relative; min-width: 0; padding: 1rem 1.1rem; background: var(--white); border: 1px solid #e6e6e2; }
.acct-address:has(> form) { grid-column: 1 / -1; }
.acct-address-head { display: flex; align-items: center; gap: 0.75rem; min-height: 1.6rem; margin: 0 0 0.4rem; }
.acct-address-head .acct-set-edit { margin-left: auto; }
.acct-address address { font-style: normal; font-size: 0.95rem; line-height: 1.55; overflow-wrap: anywhere; }
.acct-tag { display: inline-block; padding: 0.1rem 0.55rem; border-radius: 999px; background: #efefeb; color: #45453f; font-size: 0.74rem; font-weight: 700; line-height: 1.4; }
.acct-address-remove { margin: 1rem 0 0; padding-top: 1rem; border-top: 1px solid var(--line); }
@media (min-width: 640px) {
  .acct-address > .acct-address-remove { position: absolute; right: 1.1rem; bottom: 1rem; margin: 0; padding: 0; border: 0; }
}
@media (max-width: 639px) {
  .acct-addresses { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) {
  .acct-dl { grid-template-columns: minmax(0, 1fr); gap: 0.85rem; }
}

/* Sign in, create an account, reset a password: one narrow card in the middle of the page. */
.acct-page--auth { padding-block: clamp(2rem, 6vw, 4rem); }
.acct-auth { max-width: 26rem; margin-inline: auto; padding: clamp(1.5rem, 4vw, 2.25rem); }
.acct-auth-title { margin: 0 0 1.25rem; font-size: clamp(1.7rem, 4vw, 2.1rem); }
/* text-align: a link drawn as the card's button (Account deleted, Confirm your email) centres its words as a <button> does. */
.acct-submit--full { width: 100%; text-align: center; }
.acct-auth-links { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem 1rem; margin: 1.25rem 0 0; font-size: 0.92rem; }
.acct-auth-links a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; }

/* One order (the quote tool's /account/orders/<ref>, 2026-10-24): a way back to the list, the head row (the order's name and dates;
   then its pill, total and an action that leaves the page), the stage timeline once paid (the guest order page's own steps and
   look, src/order.njk), tracking and invoice, and the parts in the list's own part rows on a light panel. Scoped to the account
   pages: the guest order page keeps its own inline look. */
.acct-back { margin: 0 0 0.75rem; font-size: 0.92rem; }
.acct-back a { color: var(--ink); font-weight: 600; text-decoration: none; }
.acct-back a::before { content: "\2190\00a0"; }
.acct-back a:hover { text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 3px; }
.acct-page .order-head { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.75rem 1.25rem;
  padding-bottom: 1.1rem; margin-bottom: 1.4rem; border-bottom: 1px solid #ecece8; }
.acct-page .order-head > div:first-child { min-width: 0; }
.acct-page .order-head .acct-h2 { margin: 0 0 0.2rem; overflow-wrap: anywhere; }
.acct-page .order-head .acct-muted { margin: 0; font-size: 0.92rem; }
.acct-page .order-head-side { display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem 1rem; }
.acct-page .order-total { font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; font-variant-numeric: tabular-nums; font-weight: 700; font-size: 1.05rem; }
.acct-page .order-track { list-style: none; display: flex; margin: 0 0 1.25rem; padding: 0; }
.acct-page .order-track li { flex: 1; text-align: center; position: relative; min-width: 0; }
.acct-page .order-track li::before { content: ""; position: absolute; top: 9px; left: -50%; width: 100%; height: 2px; background: var(--line); z-index: 0; }
.acct-page .order-track li:first-child::before { display: none; }
.acct-page .ot-dot { position: relative; z-index: 1; display: block; width: 20px; height: 20px; border-radius: 50%; background: #fff;
  border: 2px solid var(--line); margin: 0 auto 0.5rem; }
.acct-page .ot-label { display: block; font-size: 0.8rem; color: var(--ink-soft); overflow-wrap: anywhere; }
.acct-page .order-track li.is-done .ot-dot { background: var(--yellow); border-color: var(--yellow); }
.acct-page .order-track li.is-done::before { background: var(--yellow); }
.acct-page .order-track li.is-current .ot-dot { border-color: var(--yellow); box-shadow: 0 0 0 4px rgba(255, 199, 0, 0.25); }
.acct-page .order-track li.is-current .ot-label, .acct-page .order-track li.is-done .ot-label { color: var(--ink); font-weight: 600; }
.acct-page .order-ship { margin: 0.25rem 0 0.75rem; font-size: 0.95rem; font-weight: 600; }
.acct-page .order-ship a { overflow-wrap: anywhere; }
.acct-page .acct-parts.order-parts { margin-top: 1.25rem; padding: 0.2rem 1rem 0.3rem; border: 1px solid #ecece8; border-radius: var(--radius); }
@media (max-width: 520px) {
  .acct-page .order-track { flex-wrap: wrap; gap: 1rem 0; }
  .acct-page .order-track li { flex-basis: 50%; }
  .acct-page .order-track li::before { display: none; }
}

/* -------------------------------------------------------------------------- */
/* Soft corners and shadows (owner 2026-10-24: "rounded edges on their buttons, makes it look softer" and "the box looks like
   it is 3D in a way like there is a shadow under it", from Xometry's quote box). Every button and field takes --radius; every
   box takes --radius and sits on --shadow-box. Notices, a box inside a box and a table's frame are rounded and flat. Full-width
   bands and bars and the client logos stay square. A new button or box joins the list it belongs to. */
/* -------------------------------------------------------------------------- */

/* Buttons and fields */
.btn, .btn-quiet, .site-nav li.nav-cta a,
.q-btn-primary, .q-price-row #q-add, .q-btn-outline, .q-btn-dark,
.q-tb-sample, .cart-toggle, .q-rot-btn, .q-tier-opt, .q-pieces-btn, .q-color-field, .q-add-drop,
.sp-fam, .mm-chip, .pill-links a, .mat-index a,
.field input, .field select, .field textarea, .sp-controls input[type="search"], .sp-request-row input,
.cl-fulfilment,
.acct-btn, .acct-icon-btn, .acct-search input, .acct-select select, .hdr-menu-panel .hdr-menu-cta a {
  border-radius: var(--radius);
}

/* The segmented controls: the outer box is rounded and the end buttons follow its inside curve (its border is 2 px), so a
   selected end button keeps the round corner and a focus ring is never cut off. */
.q-seg { border-radius: var(--radius); }
.q-seg-btn:first-child { border-top-left-radius: calc(var(--radius) - 2px); border-bottom-left-radius: calc(var(--radius) - 2px); }
.q-seg-btn:last-child { border-top-right-radius: calc(var(--radius) - 2px); border-bottom-right-radius: calc(var(--radius) - 2px); }
/* The lead time chooser stacks its buttons: the top one takes the top corners, the bottom one the bottom corners. */
#q-leadtime-seg .q-seg-btn { border-radius: 0; }
#q-leadtime-seg .q-seg-btn:first-child { border-top-left-radius: calc(var(--radius) - 2px); border-top-right-radius: calc(var(--radius) - 2px); }
#q-leadtime-seg .q-seg-btn:last-child { border-bottom-left-radius: calc(var(--radius) - 2px); border-bottom-right-radius: calc(var(--radius) - 2px); }

/* Boxes: rounded, on the shadow */
.quote-card,
.q-config-card, .q-mini-card, .q-price-panel, .q-viewer, .q-checks, .q-drop, .q-parts, .q-saved-all, .q-pieces, .q-order-table,
.q-order-aside, .q-checkout-main #cart-contact, .cart-sent,
.cs-card, .info-card, .mat-card, .testimonial-card, .cs-item, .gallery figure, .page-video, .booking-frame, .mm-scroll,
.sp-panel, .sp-detail-panel, .fi-panel,
.acct-card, .acct-menu-list {
  border-radius: var(--radius);
  box-shadow: var(--shadow-box);
}
/* A picture in a card takes the card's corners itself, inside the card's border. Never overflow: hidden on the card: a grid item
   that clips lets its row squeeze it (the color picker's tiles flattened on a phone), and the gallery photos open the lightbox
   and carry their own focus ring, which a clip would cut off. */
.cs-card > img:first-child, .sp-sw > .sp-banner, .sp-sw.sp-basic > .sp-chip {
  border-radius: calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0 0;
}
.gallery figure > img { border-radius: calc(var(--radius) - 1px); }
.gallery figure > img:not(:last-child) { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.page-video video { border-radius: calc(var(--radius) - 1px); }
.booking-frame iframe { border-radius: calc(var(--radius) - 2px); }

/* Rounded and flat: notices, a box inside a box, a table's frame, a photo in a case study */
.quote-oversize, .form-status, .quote-error, .q-rotation-panel, .cs-quote, .todo, .sp-sw, .cs-item-img, .cs-item-table-wrap, .acct-address {
  border-radius: var(--radius);
}

/* Small pieces: thumbnails, swatches, a badge, the quantity steppers */
.cl-thumb, .fi-thumb, .q-swatch, .sp-detail-imgwrap, .q-fit-badge, .cl-qty-btn, .cl-qty-input {
  border-radius: var(--radius-sm);
}
.sp-detail-imgwrap { overflow: hidden; }

/* The menu's dropdown hangs from the header bar: only its lower corners are rounded. */
@media (min-width: 1000px) {
  .site-nav .subnav, .hdr-menu-panel { border-radius: 0 0 var(--radius) var(--radius); }
}
