/* ===========================================================
   Poy — prototype stylesheet
   Palette pulled from the logo's floral accents + Cal & Lily's
   bold pink/navy direction. All colors/fonts live in :root so
   the palette (flagged as "may change later") is cheap to swap.
   =========================================================== */

@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800;900&display=swap');

:root {
  /* palette (client-provided: text / primary / secondary / accent,
     plus derived deep/pale shades for contrast and full-bleed blocks) */
  --text: #4e350f;
  --primary: #ab5e71;
  --primary-deep: #8a4a5a;
  --primary-pale: #f6e5e8;
  --secondary: #6071af;
  --secondary-deep: #3c4b80;
  --accent: #ecc84b;
  --accent-deep: #8a6a1a;
  --cream: #fdf6ec;
  --white: #ffffff;

  /* type */
  --font-display: 'Nunito', sans-serif;
  --font-body: 'Nunito', sans-serif;

  --container-width: 1180px;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* overflow-anchor: none — disables the browser's scroll anchoring for
   the whole page. Scroll anchoring tries to keep on-screen content from
   visually jumping when something above the viewport changes size, by
   nudging scrollY to compensate. Our own sticky header changes size *as
   a function of* scrollY (see .site-header/.header-row-logo below), so
   the two fight each other: header shrinks → anchoring nudges scrollY →
   our scroll handler re-evaluates against the new scrollY → header
   height changes again → anchoring nudges again. That feedback loop is
   what read as constant blinking/jumping, worst during a slow scroll
   (more frames for the loop to run in). Confirmed via an instrumented
   Playwright scroll test: without this, scrollY visibly oscillated frame
   to frame during any scroll through the header's shrink range even
   though the simulated input only ever scrolled in one direction; with
   it, scrollY was perfectly monotonic and the header shrank exactly
   once per crossing. Setting `overflow-anchor: none` scoped only to
   `.site-header` was NOT sufficient — Chrome's anchor node is chosen
   from content below the header, not the header itself, so the property
   has to be set here, on the actual scrolling element, to take effect. */
html { scroll-behavior: smooth; overflow-anchor: none; }
body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--text);
  background: var(--cream);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
h1, h2, h3 { font-family: var(--font-display); font-weight: 800; margin: 0 0 0.4em; line-height: 1.1; }
p { margin: 0 0 1em; }
ul { margin: 0; padding: 0; list-style: none; }
button { font-family: inherit; cursor: pointer; }

.container {
  max-width: var(--container-width);
  margin: 0 auto;
  padding: 0 24px;
}

.kicker {
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 0 0 0.5em;
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 30px;
  border-radius: 999px;
  border: 2px solid transparent;
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  box-shadow: 0 4px 0 rgba(0,0,0,0.12);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(1px); box-shadow: 0 2px 0 rgba(0,0,0,0.12); }

.btn-navy { background: var(--secondary-deep); color: var(--cream); }
.btn-pink { background: var(--primary-deep); color: var(--white); }
.btn-outline { background: transparent; border-color: currentColor; box-shadow: none; }

/* loading state — e.g. the collar order submit button while its fetch is
   in flight, so there's visible feedback for the gap before the success/
   error state appears. currentColor keeps the spinner matching whichever
   .btn color variant it's used on with no new color needed. */
.btn.is-loading { opacity: 0.85; cursor: default; }
.btn.is-loading:hover { transform: none; }
.btn-spinner {
  display: none;
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  opacity: 0.85;
  animation: btn-spinner-spin 0.6s linear infinite;
}
.btn.is-loading .btn-spinner { display: inline-block; }
@keyframes btn-spinner-spin { to { transform: rotate(360deg); } }

/* ---------- scalloped divider ----------
   A continuous wave made of alternating elliptical arcs (rx 70 / ry 30)
   — one bulging up, the next bulging down, tangent at each join. Wide,
   gentle rhythm (280px per full cycle) rather than tight round bumps.
   --scallop-color is set per-usage to match the section the divider is
   leading into. Used only at the top of a bold color block (max 2 per
   page). */
.scallop {
  height: 66px;
  width: 100%;
  background-color: var(--scallop-color);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='280' height='66'%3E%3Cpath d='M0,33 A70,30 0 0 1 140,33 A70,30 0 0 0 280,33 L280,66 L0,66 Z' fill='white'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='280' height='66'%3E%3Cpath d='M0,33 A70,30 0 0 1 140,33 A70,30 0 0 0 280,33 L280,66 L0,66 Z' fill='white'/%3E%3C/svg%3E");
  -webkit-mask-repeat: repeat-x;
  mask-repeat: repeat-x;
  -webkit-mask-size: 280px 66px;
  mask-size: 280px 66px;
  -webkit-mask-position: center top;
  mask-position: center top;
}

/* Wavy borders made from tiled slices of the hand-painted wave_yelow.png
   artwork (one seamless 2-bump period each) instead of the scalloped mask
   above. --about leads into the About section; --contact leads from the
   Instagram feed into the Contact section, recolored to --primary and
   given a white backing (background-color, showing through the gaps
   between bumps) so it reads as a continuation of the widget's white
   background rather than the feed section's cream. */
.wave-divider {
  height: 70px;
  width: 100%;
  background-repeat: repeat-x;
  background-position: center top;
  background-size: auto 100%;
}
.wave-divider--about {
  background-image: url('branding/wave_yelow_tile.png');
}
.wave-divider--contact {
  background-color: var(--white);
  background-image: url('branding/wave_primary_tile.png');
}

/* ===========================================================
   Header
   =========================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
}

/* anchor-nav targets on the landing page — without this, jumping to
   #about/#services/#feed/#contact (nav links, cross-page links like
   collars.html's) lands the section flush with the very top of the
   viewport, which the sticky header then overlaps, hiding the heading
   the click was supposed to reveal. scroll-margin-top shifts the landing
   spot down by the header's live height (the same --header-height custom
   property the price panel uses, kept in sync by script.js) plus a
   little breathing room, so the heading always ends up visible just
   below the header regardless of its current shrunk/unshrunk state. The
   203px fallback matches the header's unscrolled height, for the moment
   before script.js has run (e.g. landing directly on a fragment URL). */
#about, #services, #feed, #contact {
  scroll-margin-top: calc(var(--header-height, 203px) + 16px);
}

/* Row 1 — logo only, centered, no color block. Shrinks to half size once
   the page is scrolled (see script.js), freeing up vertical space; the
   blue nav row below is untouched by this.
   The row's own `height` is the only thing that animates via layout (one
   property, not two) — the logo itself shrinks via `transform: scale()`,
   which is compositor-only and never triggers layout, so the browser has
   far less work to redo on every animation frame while this sticky header
   is transitioning during an active scroll. Padding is fixed (not part of
   the transition) since flex centering handles vertical alignment on its
   own; height values below are 14+130+8=152 unscrolled, 7+65+4=76 scrolled
   (border-box, per the global reset), preserving the original sizing. */
.header-row-logo {
  display: flex;
  justify-content: center;
  align-items: center;
  background: var(--cream);
  height: 152px;
  overflow: hidden;
  transition: height 0.25s ease;
  will-change: height;
}
.logo-link { display: flex; }
.logo-img {
  height: 130px;
  width: auto;
  transform-origin: center;
  transition: transform 0.25s ease;
  will-change: transform;
}
.logo-link-mobile { display: none; }

.site-header.is-scrolled .header-row-logo { height: 76px; }
.site-header.is-scrolled .header-row-logo .logo-img { transform: scale(0.5); }

/* Row 2 — nav links + language switcher, on the blue color block */
.header-row-nav {
  background: var(--secondary);
  border-bottom: 3px solid var(--secondary-deep);
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 12px;
  padding-bottom: 12px;
}

/* display: contents unwraps the nav-left/nav-right grouping boxes so
   each link becomes a direct flex child of .header-inner, alongside
   the language switcher — that's what makes justify-content: space-between
   spread everything out edge to edge. */
.nav-side { display: contents; }
.nav-side a, .mobile-nav a {
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* row 2 sits on the blue block, so its links need light text; the mobile
   dropdown panel is its own cream surface and keeps the default dark text */
.header-inner .nav-side a { color: var(--cream); }
.header-inner .nav-side a:hover { color: var(--accent); }
.mobile-nav a:hover { color: var(--primary-deep); }

.mobile-nav { display: none; }

/* language switcher — styled like a nav link (current language + arrow),
   opens a small dropdown with the other language */
.lang-switch { position: relative; }
.lang-switch-trigger {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: none;
  padding: 0;
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--cream);
  cursor: pointer;
}
.lang-switch-trigger:hover { color: var(--accent); }
.lang-switch-trigger svg { width: 14px; height: 14px; transition: transform 0.15s ease; }
.lang-switch.open .lang-switch-trigger svg { transform: rotate(180deg); }

.lang-switch-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 12px;
  background: var(--white);
  border-radius: 12px;
  box-shadow: 0 10px 24px rgba(23,28,58,0.2);
  padding: 6px;
  min-width: 64px;
  z-index: 10;
}
.lang-switch.open .lang-switch-menu { display: block; }
.lang-switch-option {
  display: block;
  width: 100%;
  border: none;
  background: transparent;
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--secondary-deep);
  padding: 8px 12px;
  border-radius: 8px;
  cursor: pointer;
  text-align: center;
}
.lang-switch-option:hover { background: var(--primary-pale); }
.lang-switch-option.is-current { display: none; }

/* the mobile dropdown copy sits on the cream panel, not the blue bar */
.mobile-nav .lang-switch-trigger { color: var(--text); }
.mobile-nav .lang-switch-trigger:hover { color: var(--primary-deep); }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  background: transparent;
  border: none;
}
.nav-toggle span {
  display: block;
  height: 3px;
  border-radius: 3px;
  background: var(--cream);
}

/* ===========================================================
   Hero
   =========================================================== */
.hero {
  background: var(--cream);
}
.hero-inner { padding-top: 8px; padding-bottom: 56px; }

/* full-bleed, edge-to-edge — no rounded corners, no card framing,
   no captions; the photos carry the section on their own */
.hero-highlights {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin-bottom: 56px;
}
.hero-highlight-link { display: block; overflow: hidden; }
.hero-highlight-link img {
  width: 100%;
  height: 320px;
  object-fit: cover;
  display: block;
  transition: transform 0.3s ease;
}
.hero-highlight-link:hover img { transform: scale(1.04); }

.hero-text { max-width: 700px; margin: 0 auto; text-align: center; }
.hero-text .btn { margin-top: 4px; }
.hero h1 {
  font-size: clamp(2.4rem, 4.5vw, 3.6rem);
  color: var(--secondary-deep);
}
.hero-sub {
  font-size: 1.05rem;
  max-width: 56ch;
  margin-left: auto;
  margin-right: auto;
}

/* ===========================================================
   Services
   =========================================================== */
.services { background: var(--cream); }
.services .container { padding-top: 72px; padding-bottom: 64px; }
.section-heading { text-align: center; max-width: 640px; margin: 0 auto 44px; }
.section-heading h2 { font-size: clamp(2.3rem, 4vw, 3rem); color: var(--secondary-deep); }
.section-heading .kicker { color: var(--primary-deep); }

.card-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  align-items: start;
}
/* the new service photos already have their own decorative frame baked
   into the image itself, so no CSS border/frame here — just show them
   at their natural (near-square) aspect ratio so nothing gets cropped. */
.card-img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
}
.card-caption { text-align: center; margin-top: 16px; }
.card h3 {
  font-size: 1rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.3;
  margin: 0 0 6px;
  color: var(--secondary-deep);
}
.card p.desc { font-size: 15px; margin: 0; color: var(--text); }
.card-cta { margin-top: 14px; }

/* ===========================================================
   Collar order page
   =========================================================== */
.collar-hero { background: var(--cream); padding: 28px 0 20px; text-align: center; }
.collar-hero .kicker { color: var(--primary-deep); margin-bottom: 0.25em; }
.collar-hero h1 { color: var(--secondary-deep); font-size: clamp(1.8rem, 3.2vw, 2.5rem); margin-bottom: 0.3em; }
.collar-hero-sub { max-width: 56ch; margin: 0 auto; font-size: 0.95rem; }

.order-section { background: var(--cream); padding-bottom: 40px; }
/* wider than a typical text column on purpose — more room per row means
   the option/fabric grids below need fewer rows to show everything, so
   there's less to scroll through to see all the choices */
.order-form { max-width: 900px; margin: 0 auto; }

/* 4-step flow — one .form-step visible at a time (script.js toggles the
   `hidden` attribute). .step-progress is the persistent graphical stepper
   above them (circles + connecting lines, filled/checked as steps
   complete); each .form-step's own heading below it is the per-step
   content title, not a position indicator. */
.form-step { scroll-margin-top: 130px; }
.step-heading { color: var(--secondary-deep); font-size: clamp(1.4rem, 2.5vw, 1.8rem); margin: 0 0 20px; }

.step-progress {
  display: flex;
  align-items: flex-start;
  list-style: none;
  margin: 0 0 28px;
  padding: 0;
}
.step-progress-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}
.step-progress-circle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 2px solid var(--primary-pale);
  background: var(--white);
  color: var(--text);
  font-weight: 800;
  font-size: 0.95rem;
  flex-shrink: 0;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.step-progress-check { display: none; width: 16px; height: 16px; }
.step-progress-label {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text);
  opacity: 0.55;
  white-space: nowrap;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.step-progress-line {
  flex: 1 1 20px;
  min-width: 16px;
  height: 2px;
  background: var(--primary-pale);
  margin-top: 16px;
  transition: background 0.15s ease;
}

.step-progress-item.is-current .step-progress-circle { border-color: var(--secondary-deep); color: var(--secondary-deep); }
.step-progress-item.is-current .step-progress-label { opacity: 1; color: var(--secondary-deep); }

.step-progress-item.is-complete .step-progress-circle { background: var(--secondary-deep); border-color: var(--secondary-deep); }
.step-progress-item.is-complete .step-progress-number { display: none; }
.step-progress-item.is-complete .step-progress-check { display: block; color: var(--cream); }
.step-progress-item.is-complete .step-progress-label { opacity: 1; color: var(--secondary-deep); }
.step-progress-item.is-complete + .step-progress-line { background: var(--secondary-deep); }
.step-nav { display: flex; align-items: center; justify-content: flex-end; gap: 16px; margin-top: 20px; }
/* pushes itself flush left while the Next/Submit button stays flush
   right, so a lone Next button (step 1 has no Back) still lands on the
   same side it does everywhere else */
.step-back-link {
  margin-right: auto;
  background: transparent;
  border: none;
  padding: 0;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--secondary-deep);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.step-back-link:hover { color: var(--primary-deep); }

/* fieldset/legend carry a lot of UA default border/padding/inset-legend
   styling — this class is used on both <fieldset> and plain <div> section
   wrappers, so it resets those defaults uniformly across both. */
.form-section {
  border: none;
  margin: 0 0 20px;
  padding: 0 0 20px;
  border-bottom: 1px solid rgba(171,94,113,0.18);
}
.form-section:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.form-legend {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--secondary-deep);
  margin: 0 0 8px;
  padding: 0;
}

/* marks a required field/section's legend or label — a CSS-generated
   character rather than extra markup, so it survives i18n.js overwriting
   the element's textContent on every language switch */
.is-required::after {
  content: " *";
  color: var(--primary-deep);
  font-weight: 800;
}

.form-note { font-size: 0.95rem; color: var(--text); opacity: 0.75; margin: 6px 0 0; }
/* informational note — light blue rather than pink, so it doesn't read as
   a warning */
.form-note--soft {
  background: rgba(96, 113, 175, 0.08); /* --secondary at low alpha */
  border-radius: 10px;
  padding: 8px 12px;
  opacity: 1;
}
.field-help { font-size: 0.95rem; opacity: 0.65; margin: 4px 0 0; }
.field-help a { text-decoration: underline; text-underline-offset: 2px; }
.field-help a:hover { color: var(--primary-deep); }

/* pull-strength slider (collars.html step 1) — 3 stops, labels spread
   underneath so each sits under its stop */
.pull-slider {
  display: block;
  width: 100%;
  margin: 8px 0 2px;
  accent-color: var(--primary);
  cursor: pointer;
}
.pull-slider-labels {
  display: flex;
  justify-content: space-between;
  font-size: 0.95rem;
}

.field { margin-bottom: 10px; }
.field:last-child { margin-bottom: 0; }
.field label,
.field-label {
  display: block;
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--secondary-deep);
  margin-bottom: 4px;
}
.field input[type="text"],
.field input[type="email"],
.field input[type="tel"],
.field input[type="number"],
.field select,
.field textarea {
  width: 100%;
  border: 2px solid var(--primary-pale);
  background: var(--white);
  border-radius: 10px;
  padding: 8px 12px;
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--text);
  transition: border-color 0.15s ease;
}
.field input:focus,
.field select:focus,
.field textarea:focus { outline: none; border-color: var(--primary-deep); }
.field textarea { min-height: 70px; resize: vertical; }
.field select { cursor: pointer; }

/* validation — inline, next to the field it belongs to, never a generic
   top-of-page banner. --primary-deep doubles as the "error" color since
   the palette has no dedicated red; it reads clearly enough against the
   cream/white field backgrounds. */
.field-error {
  color: var(--primary-deep);
  font-size: 0.95rem;
  font-weight: 700;
  margin: 4px 0 0;
}
.field input.is-invalid,
.field textarea.is-invalid { border-color: var(--primary-deep); }
/* required radio/checkbox groups with no default selection (width,
   hardware, delivery use .option-card; fabric uses its own accordion
   markup) — the whole group gets a colored border when nothing's picked */
.form-section.is-invalid .option-card { border-color: var(--primary-deep); }
.form-section.is-invalid .fabric-tabs .fabric-tab { border-color: var(--primary-deep); }
.form-section.is-invalid .hardware-chip { border-color: var(--primary-deep); }

/* radio/checkbox "cards" used for width, hardware, delivery and the
   nametag sub-choices — the whole card is clickable since the input
   sits inside its <label>; :has() gives the checked one a filled look
   without needing JS just to toggle a class. */
.option-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
}
.option-grid--two { grid-template-columns: repeat(2, 1fr); }
.option-grid--three { grid-template-columns: repeat(3, 1fr); }
.option-grid--compact { gap: 6px; margin-top: 6px; }

.option-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  border: 2px solid var(--primary-pale);
  background: var(--white);
  border-radius: 12px;
  padding: 8px 10px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.option-card input { accent-color: var(--primary-deep); margin-right: 4px; }
.option-card:has(input:checked) { border-color: var(--primary-deep); background: var(--primary-pale); }
.option-card-title { font-weight: 700; font-size: 1rem; }
.option-card-price { font-size: 0.95rem; opacity: 0.75; }/* width example cards — radio + label on top so it reads as a choice
   first, then the example photo bleeding to the card's side and bottom
   edges (negative margin cancels .option-card's own padding), same idea
   as the fabric/hardware swatch photos */
.option-card--image { padding-bottom: 0; overflow: hidden; }
.option-card-head { display: flex; align-items: center; gap: 4px; }
.option-card-img {
  width: calc(100% + 20px);
  max-width: none; /* global img max-width: 100% would clip the bleed and leave a strip on the right */
  margin: 6px -10px 0;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}
/* font sample photos are wide nametag shots — keep their own shape so the
   4:3 crop doesn't cut off the ends of the embroidered text */
.option-card-img--natural { aspect-ratio: auto; }
/* 40mm-silver lock: JS disables the input and adds this class so the
   reason (the note above the group) is visible, not just a mystery gray */
.option-card.is-disabled { opacity: 0.5; cursor: not-allowed; background: var(--cream); }
.option-card.is-disabled input { pointer-events: none; }

/* fabric catalogue — one category's swatches visible at a time, switched
   via a row of tabs (script.js) rather than the accordion this replaced;
   .fabric-panel[hidden] just uses the browser's native hidden behavior */
.fabric-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}
.fabric-tab {
  border: 2px solid var(--primary-pale);
  background: var(--white);
  color: var(--secondary-deep);
  border-radius: 999px;
  padding: 8px 14px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.95rem;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.fabric-tab:hover { border-color: var(--primary-deep); }
.fabric-tab.is-active { background: var(--secondary-deep); border-color: var(--secondary-deep); color: var(--cream); }

.fabric-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 8px;
}
/* the swatch photo is the main identifier now (no code shown to the
   customer) — the radio floats over its top-left corner rather than
   taking its own row, so the image can run the full width of the card.
   .fabric-chip is a plain div (not the label) so the enlarge button can
   sit as its sibling instead of nesting inside the label — nesting a
   button inside a label makes click-forwarding to the radio unreliable
   across browsers. .fabric-chip-select is `display: contents` so its
   children (input/image/name) lay out exactly as if they were direct
   flex children of .fabric-chip, keeping the original card layout. */
.fabric-chip {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 2px solid var(--primary-pale);
  background: var(--white);
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  font-size: 1rem;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.fabric-chip-select { display: contents; }
.fabric-chip:has(input:checked) {
  border-color: var(--primary-deep);
  box-shadow: 0 0 0 2px var(--primary-pale) inset;
}
.fabric-chip:has(input:checked) .fabric-name-row { background: var(--primary-pale); }
.fabric-swatch {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
  background: var(--cream);
}
/* radio sits in the white name row, not over the (often busy) pattern
   photo, where it used to be hard to notice */
.fabric-name-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 6px;
  padding: 5px 8px 6px;
  transition: background 0.15s ease;
}
.fabric-name-row input { margin: 0; flex-shrink: 0; accent-color: var(--primary-deep); }
.fabric-name { color: var(--text); }
.fabric-note { font-size: 0.95rem; opacity: 0.65; flex-basis: 100%; }

/* set by the maker via patterns/catalogue.json (status: "out_of_stock") —
   same greyed-out treatment as .option-card.is-disabled (the 40mm/silver
   hardware lock), plus a small badge since there's no adjacent note
   explaining the disabled radio the way that lock has */
.fabric-chip.is-out-of-stock { opacity: 0.55; cursor: not-allowed; }
.fabric-chip.is-out-of-stock .fabric-swatch { filter: grayscale(60%); }
.fabric-out-of-stock-badge {
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--primary-deep);
  opacity: 1;
}
.fabric-grid-empty { grid-column: 1 / -1; margin: 0; }

/* hardware catalogue — same card shape as the fabric chips (image, radio
   + name in the white row below), fetched from hardware/catalogue.json.
   Product photos rather than repeating pattern swatches, so `contain`
   instead of `cover` — cropping a clasp in half would look broken in a
   way cropping a fabric texture doesn't. */
.hardware-grid {
  display: grid;
  /* always exactly 4 hardware options (unlike the fabric catalogue, which
     can grow), so a fixed 4-column split fills the row edge to edge
     instead of packing tight and leaving empty space to the right */
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 8px;
}
.hardware-chip {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 2px solid var(--primary-pale);
  background: var(--white);
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  font-size: 1rem;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.hardware-chip-select { display: contents; }
.hardware-chip:has(input:checked) {
  border-color: var(--primary-deep);
  box-shadow: 0 0 0 2px var(--primary-pale) inset;
}
.hardware-chip:has(input:checked) .hardware-name-row { background: var(--primary-pale); }
.hardware-swatch {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  display: block;
  background: var(--white);
  padding: 10px;
}
.hardware-name-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 6px;
  padding: 5px 8px 6px;
  transition: background 0.15s ease;
}
.hardware-name-row input { margin: 0; flex-shrink: 0; accent-color: var(--primary-deep); }
.hardware-name { color: var(--text); }
.hardware-note { font-size: 0.95rem; opacity: 0.65; flex-basis: 100%; }
/* set by the maker via hardware/catalogue.json (status: "out_of_stock"),
   or by the 40mm-silver lock (.is-disabled, see applyWidthLock()) —
   same greyed-out treatment either way */
.hardware-chip.is-out-of-stock,
.hardware-chip.is-disabled { opacity: 0.5; cursor: not-allowed; background: var(--cream); }
.hardware-chip.is-out-of-stock .hardware-swatch { filter: grayscale(60%); }
.hardware-out-of-stock-badge {
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--primary-deep);
  opacity: 1;
}

.fabric-enlarge {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(253,246,236,0.85);
  color: var(--secondary-deep);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.fabric-enlarge svg { width: 13px; height: 13px; }
.fabric-enlarge:hover { background: var(--cream); color: var(--primary-deep); }

/* fabric lightbox — full-screen viewer opened from any .fabric-enlarge
   button; script.js scopes prev/next to whichever category tab is open */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: rgba(78,53,15,0.85);
  padding: 24px;
}
/* [hidden] and a plain class selector are equal specificity, so without
   this the `display: flex` above would win and show the lightbox even
   while the `hidden` attribute is set on page load */
.lightbox[hidden] { display: none; }
.lightbox-content {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  max-width: min(820px, 85vw);
}
.lightbox-img {
  max-width: 100%;
  max-height: 80vh;
  display: block;
  border-radius: 12px;
  background: var(--cream);
  box-shadow: 0 12px 32px rgba(0,0,0,0.35);
}
.lightbox-caption { margin: 0; color: var(--cream); font-weight: 700; font-size: 0.95rem; text-align: center; }
.lightbox-close,
.lightbox-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border: none;
  border-radius: 50%;
  background: rgba(253,246,236,0.15);
  color: var(--cream);
  cursor: pointer;
  transition: background 0.15s ease;
}
.lightbox-close:hover,
.lightbox-nav:hover { background: rgba(253,246,236,0.3); }
.lightbox-nav { width: 44px; height: 44px; }
.lightbox-nav svg { width: 22px; height: 22px; }
.lightbox-close {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 40px;
  height: 40px;
}
.lightbox-close svg { width: 18px; height: 18px; }

.nametag-details {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed var(--primary-pale);
}
.nametag-example-row {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  margin-bottom: 10px;
}
.nametag-example-text { flex: 1; min-width: 0; }
.nametag-example-text .field { margin-bottom: 0; }
.nametag-example-img {
  width: 210px;
  height: 96px;
  object-fit: cover;
  border-radius: 12px;
  flex-shrink: 0;
}

/* Live price panel — sidebar next to the form on desktop, a collapsed
   fixed bar (just the total) on mobile that expands to the breakdown.
   Both states share the same markup; only position/layout changes, see
   the max-width: 900px override below. */
.order-layout {
  display: grid;
  grid-template-columns: 1fr 260px;
  gap: 40px;
  align-items: start;
}
.order-success {
  grid-column: 1 / -1;
  max-width: 56ch;
  margin: 40px auto;
  padding: 32px 24px;
  text-align: center;
  background: var(--white);
  border: 2px solid var(--primary-pale);
  border-radius: 16px;
}
.order-success h2 { color: var(--secondary-deep); margin-bottom: 0.5em; }
.price-panel {
  position: sticky;
  top: calc(var(--header-height, 90px) + 12px);
  background: var(--white);
  border: 2px solid var(--primary-pale);
  border-radius: 16px;
  padding: 16px 18px;
}
/* lives inside .price-details so it's part of the same sticky box and
   scrolls as one unit with the price breakdown, instead of being a
   separate sibling that could slide up behind it mid-scroll */
.price-panel-note {
  margin: 12px 0 0;
  padding-top: 12px;
  border-top: 1px dashed var(--primary-pale);
}
.price-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  background: transparent;
  border: none;
  padding: 0;
  cursor: default;
  font-family: var(--font-body);
  color: var(--secondary-deep);
}
.price-summary-label {
  font-weight: 800;
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.price-summary-total { font-weight: 800; font-size: 1.35rem; color: var(--primary-deep); }
/* only meaningful once the panel becomes a collapsible mobile bar */
.price-toggle-icon { display: none; width: 18px; height: 18px; flex-shrink: 0; transition: transform 0.15s ease; }

.price-details { margin-top: 12px; }
/* the details pane starts `hidden` in the markup (mobile-collapsed
   default) — on desktop it should always read as expanded regardless,
   since there's no bottom-bar space constraint to collapse for */
.price-details[hidden] { display: block; }
.price-lines { margin: 0; }
.price-line {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.95rem;
  padding: 5px 0;
  border-top: 1px solid var(--primary-pale);
}
.price-lines .price-line:first-child { border-top: none; }
/* display: flex above would otherwise override the hidden attribute that
   updatePrice() uses for the optional lines (reinforcement/nametag/delivery) */
.price-line[hidden] { display: none; }

/* ===========================================================
   Mending price list
   =========================================================== */
.mending-section { background: var(--cream); padding: 40px 0 72px; }
.mending-content { max-width: 720px; margin: 0 auto; }
.mending-content h2 { color: var(--secondary-deep); margin-top: 1.2em; }
.mending-content h3 { color: var(--primary-deep); font-size: 1.1rem; margin-top: 1.4em; margin-bottom: 0.5em; }
.price-table-wrap { overflow-x: auto; margin-bottom: 1.2em; }
.price-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--white);
  border: 2px solid var(--primary-pale);
  border-radius: 12px;
  overflow: hidden;
}
.price-table td {
  padding: 10px 16px;
  font-size: 0.95rem;
  border-top: 1px solid var(--primary-pale);
}
.price-table tr:first-child td { border-top: none; }
.price-table td:last-child { text-align: right; white-space: nowrap; font-weight: 700; color: var(--secondary-deep); }
.mending-cta { margin-top: 1.5em; }

/* ===========================================================
   Legal / Terms & Conditions
   =========================================================== */
.legal-section { background: var(--cream); padding: 40px 0 72px; }
.legal-content { max-width: 720px; margin: 0 auto; }
.legal-content h2 { color: var(--secondary-deep); font-size: 1.25rem; margin-top: 1.6em; }
.legal-content h2:first-child { margin-top: 0; }
.legal-content h3 { color: var(--primary-deep); font-size: 1.05rem; margin-top: 1.2em; margin-bottom: 0.5em; }
.legal-content p { font-size: 0.95rem; }
.legal-content ul { list-style: disc; padding-left: 1.4em; margin: 0 0 1em; }
.legal-content li { font-size: 0.95rem; margin-bottom: 0.3em; }
.legal-note {
  background: var(--white);
  border: 2px solid var(--primary-pale);
  border-radius: 12px;
  padding: 16px 20px;
  font-size: 0.88rem;
  font-style: italic;
  margin-top: 2em;
}

/* ===========================================================
   Reviews
   =========================================================== */
.reviews { background: var(--primary); padding: 72px 0; }
/* arrows are pinned to the edges of this full-width wrapper, not to the
   (narrower, variable-width) text block, so they never shift position
   when a review's width changes. */
.reviews-carousel { position: relative; }
.review-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: none;
  background: var(--cream);
  color: var(--secondary-deep);
  cursor: pointer;
  transition: background 0.15s ease;
}
.review-arrow-prev { left: 0; }
.review-arrow-next { right: 0; }
.review-arrow svg { width: 22px; height: 22px; }
.review-arrow:hover { background: var(--accent); }

/* fixed-height stage sized to the shorter, typical review (review 2) so
   the section doesn't jump height between slides. The one long review
   (review 1) gets extra width instead, via .is-wide, to help it fit. */
.review-track {
  max-width: 760px;
  margin: 0 auto;
  padding: 0 64px;
  min-height: 220px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}
/* review 1 is much longer than the rest — give it the full available
   width plus a slightly smaller font, together enough to land at the
   same section height as the other (shorter) reviews. */
.review-track.is-wide { max-width: none; }
.review-track.is-wide .review-text { font-size: 1.3rem; }
.review-slide { display: none; }
.review-slide.is-active { display: block; }
.review-text {
  font-size: clamp(1.15rem, 2vw, 1.5rem);
  line-height: 1.5;
  color: var(--white);
  margin: 0 0 18px;
}
.review-handle {
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--secondary-deep);
  margin: 0;
}

.review-dots { display: flex; justify-content: center; gap: 10px; margin-top: 36px; }
.review-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(253,246,236,0.45);
}
.review-dot.is-active { background: var(--cream); }

/* ===========================================================
   Instagram feed
   =========================================================== */
.feed { background: var(--cream); }
.feed .container { padding-top: 72px; }
.feed-widget {
  width: 100%;
  background: var(--white);
  padding-bottom: 56px;
}
.feed-widget [class*="elfsight-app-"] { min-height: 500px; display: block; }

/* ===========================================================
   About
   =========================================================== */
.about { background: #ffd64c; padding-top: 30px; padding-bottom: 94px; }
.about-inner {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: 52px;
  align-items: end;
}
/* pulled down by the section's own bottom padding so the photo reaches
   the true edge of the yellow block instead of stopping level with the
   (shorter) text column and floating above the section's bottom edge */
.about-photo { position: relative; z-index: 1; margin-bottom: -94px; }
.about-text { position: relative; z-index: 2; }
.about-photo-img {
  width: 90%;
  height: 523px;
  object-fit: contain;
  object-position: bottom;
}

/* bleeds past the photo into the bio text column like a stamp — sits
   above the photo but behind the text, see .about-text z-index below */
.badge {
  position: absolute;
  bottom: -20px;
  right: -80px;
  width: 396px;
  height: auto;
  transform: rotate(-9deg);
}

.about-text h2 { color: var(--secondary-deep); font-size: clamp(2.3rem, 4vw, 3rem); }
.about-text .kicker { color: var(--secondary-deep); }
.about-text p { max-width: 54ch; }

/* ===========================================================
   Contact
   =========================================================== */
.contact { background: var(--primary); padding: 72px 0; text-align: center; }
.contact .kicker { color: var(--white); }
.contact h2 { color: var(--secondary-deep); font-size: clamp(2.3rem, 4vw, 3rem); }
.contact p { max-width: 50ch; margin-left: auto; margin-right: auto; }
.contact .btn-outline { color: var(--cream); }
.contact-actions {
  display: flex;
  gap: 18px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 24px;
}

/* ===========================================================
   Footer
   =========================================================== */
.site-footer {
  background: var(--secondary-deep);
  color: var(--cream);
  padding: 40px 0 28px;
}
.footer-inner {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 32px;
}
.footer-logo { height: 100px; filter: brightness(0) invert(1); opacity: 0.9; }

/* four equal columns: logo / dog collars & pricelists / legal / contact */
.footer-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  font-weight: 700;
  font-size: 1rem;
}
.footer-col a:hover { color: var(--accent); }
/* "Cookie settings" — a <button> (it opens the banner, it isn't a link)
   dressed to match the footer links around it */
.footer-link-button {
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  font: inherit;
  text-align: inherit;
  cursor: pointer;
}
.footer-link-button:hover { color: var(--accent); }

/* cookie consent banner (consent.js) — bottom-left card on desktop, full
   width on phones; sits above the collar page's fixed price bar (z 40) */
.cookie-banner {
  position: fixed;
  left: 16px;
  bottom: 16px;
  z-index: 60;
  width: min(420px, calc(100% - 32px));
  background: var(--white);
  color: var(--text);
  border: 2px solid var(--primary-pale);
  border-radius: 20px;
  box-shadow: 0 8px 28px rgba(23,28,58,0.18);
  padding: 18px 20px;
}
.cookie-banner[hidden] { display: none; }
.cookie-banner-title { font-weight: 800; font-size: 1.05rem; margin: 0 0 6px; }
.cookie-banner-text { font-size: 0.95rem; margin: 0 0 14px; line-height: 1.45; }
.cookie-banner-text a { text-decoration: underline; text-underline-offset: 2px; }
.cookie-banner-text a:hover { color: var(--primary-deep); }
.cookie-banner-actions { display: flex; gap: 10px; }
.cookie-btn { flex: 1; justify-content: center; padding: 11px 16px; font-size: 0.85rem; }
@media (max-width: 640px) {
  .cookie-banner {
    left: 0;
    bottom: 0;
    width: 100%;
    border-radius: 20px 20px 0 0;
    border-width: 2px 0 0;
    padding-bottom: calc(18px + env(safe-area-inset-bottom));
  }
}

.footer-socials { display: flex; gap: 10px; }
.social-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(253,246,236,0.12);
  color: var(--cream);
  transition: background 0.15s ease, color 0.15s ease;
}
.social-icon svg { width: 18px; height: 18px; }
/* clear invert on hover: solid cream badge with a dark icon, swapping
   which color is foreground vs background rather than fading either out */
.footer-col a.social-icon:hover { background: var(--cream); color: var(--secondary-deep); }
.footer-note {
  grid-column: 1 / -1;
  text-align: center;
  margin-top: 24px;
  font-size: 0.95rem;
  opacity: 0.65;
}

/* ===========================================================
   Responsive
   =========================================================== */
@media (max-width: 900px) {
  .hero-highlight-link img { height: 240px; }
  .card-grid { grid-template-columns: 1fr 1fr; }
  .about-inner { grid-template-columns: 1fr; }
  .about-photo { max-width: 420px; margin: 0 auto; }
  .badge { width: 220px; height: auto; bottom: -30px; right: -110px; }

  /* price panel: sticky sidebar → fixed bottom bar, collapsed to just
     the total by default (native `hidden` on .price-details, toggled by
     script.js, which also hides the delivery note nested inside it) —
     room for it is reserved via the form's padding-bottom below so the
     last field/notes textarea never sits underneath it */
  .order-layout { grid-template-columns: 1fr; }
  .order-form { padding-bottom: 90px; }
  .price-panel {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 40;
    border-radius: 20px 20px 0 0;
    border-width: 2px 0 0;
    box-shadow: 0 -6px 20px rgba(23,28,58,0.15);
    padding: 12px 20px calc(12px + env(safe-area-inset-bottom));
  }
  .price-summary { cursor: pointer; }
  .price-toggle-icon { display: block; }
  .price-panel[data-expanded="true"] .price-toggle-icon { transform: rotate(180deg); }
  .price-details[hidden] { display: none; }
  .price-details { max-height: 45vh; overflow-y: auto; }

  /* 4 equal columns get cramped on tablet widths — pair them up instead */
  .footer-inner { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 640px) {
  /* logo + hamburger share one row: hide the standalone logo row and
     show the inline mobile logo inside the nav row instead */
  .header-row-logo { display: none; }
  .logo-link-mobile { display: flex; }
  .logo-img { height: 44px; }
  /* keep the lighter cream bar on mobile instead of the blue block —
     the hamburger icon needs a dark color to stay visible against it */
  .header-row-nav { background: var(--cream); }
  .nav-toggle span { background: var(--secondary-deep); }
  .header-inner { display: flex; justify-content: space-between; align-items: center; }
  .nav-side { display: none; }
  /* the bar's own lang-switch collapses into the mobile dropdown instead */
  .header-inner > .lang-switch { display: none; }
  .mobile-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--cream);
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
    padding: 20px 24px 28px;
    border-bottom: 3px solid var(--secondary-deep);
    display: none;
  }
  .mobile-nav.open { display: flex; }
  .nav-toggle { display: flex; }

  /* one highlight photo visible at a time, swipeable, instead of all
     three stacked — keeps the hero headline above the fold */
  .hero-highlights {
    display: flex;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin-bottom: 32px;
  }
  .hero-highlights::-webkit-scrollbar { display: none; }
  .hero-highlight-link { flex: 0 0 100%; scroll-snap-align: start; }
  .hero-highlight-link img { height: 220px; }

  .card-grid { grid-template-columns: 1fr; }
  .contact-actions { flex-direction: column; align-items: center; }
  .footer-inner { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .footer-col { align-items: center; }

  /* shrink further than the tablet size above; nudged left (photo size
     itself unchanged) so the badge below has room to grow into the space
     freed up on the right instead of sitting centered under it */
  .about-photo { max-width: 250px; margin-bottom: -70px; transform: translateX(-20px); }
  /* stays on the tablet's bottom-right hang (right/bottom inherited from
     the 900px breakpoint), just sized up, so it balances the photo's
     leftward shift; it's on the shared z-index:1 layer below .about-text
     (see z-index note above), so it can never sit over the heading even
     where it overlaps the photo/text column horizontally */
  .badge { width: 180px; }
  /* height was a fixed 523px sized for the desktop photo, which left empty
     space above the (bottom-anchored) image at mobile widths. 390px
     matches the image's own aspect ratio at 250px wide, so it fills the
     box with no dead space; the bottom-bleed pull (margin above) and the
     section's padding-bottom are scaled to match so the flush-to-edge
     bleed effect still lines up at the new, smaller size. */
  .about-photo-img { height: 295px; }
  .about { padding-top: 0; padding-bottom: 70px; }
  /* the bleed margin above cancels almost all of .about-inner's row gap,
     leaving the heading crowding the photo — push it back down. */
  .about-text { margin-top: 40px; }

  /* smaller type + slimmer arrow gutters so the longest review (review 1)
     doesn't force the section to towering heights on narrow screens */
  .reviews { padding: 48px 0; }
  .review-arrow { width: 34px; height: 34px; }
  .review-arrow svg { width: 16px; height: 16px; }
  .review-track { padding: 0 42px; min-height: 190px; }
  .review-text,
  .review-track.is-wide .review-text { font-size: 1rem; line-height: 1.45; }

  /* 4 equal columns get too cramped on a phone-width row — 2x2 instead,
     still filling the row edge to edge */
  .hardware-grid { grid-template-columns: repeat(2, 1fr); }

  /* let the connecting lines shrink first on a narrow phone rather than
     the circles/labels, which need to stay tap-friendly and readable */
  .step-progress-line { flex-basis: 8px; min-width: 8px; }
  .step-progress-circle { width: 30px; height: 30px; }

  /* nametag example photo stacks above the text field instead of sitting
     beside it once there's no room for both at a readable width */
  .nametag-example-row { flex-direction: column; }
  .nametag-example-img { width: 100%; height: auto; aspect-ratio: 4 / 3; }
}
