/* ============================================================================
   EUROCUTPRECISION — site design system (non-homepage surfaces)
   ----------------------------------------------------------------------------
   flagship.css is the approved visual language. It shipped scoped to two
   prototype templates: the homepage and one product page. Every other page ran
   the earlier design, so the site read as two websites stitched together —
   different page widths, two card styles, two heading scales, two button
   weights, and a header that changed height when you followed a link.

   This sheet carries that language onto the rest of the site. It is loaded
   AFTER flagship.css and only by non-homepage pages, which is deliberate:

     THE HOMEPAGE IS APPROVED AND LOCKED. It does not load this file, so no
     rule here can reach it. That is the regression guarantee — not a promise
     to be careful, a structural impossibility. `tests/designSystem.test.js`
     fails if index.html ever links this sheet.

   Two jobs, in order:

     1. BRIDGE — legacy components (.card, .btn, .section, .form-field, tables)
        are re-expressed in the flagship tokens, so pages that were never
        rewritten still inherit the approved look.
     2. COMPONENTS — the new surfaces this sprint needed: page introductions,
        the family range selector, the distribution map environment, grouped
        forms, the technical table, the restrained 3D cross-link.

   Everything is scoped under .fx-page. Nothing here is a global selector.
   ========================================================================== */

/* ---------------------------------------------------------------- measure --
   One page width, everywhere.

   The header and footer were already 1720px with a fluid gutter; legacy
   content was 1280px with a fixed one. The two never lined up, so a logo sat
   90px to the left of the H1 below it on every page that was not the homepage.
   Content now shares the chrome's measure, and the things that must not be
   1720px wide — prose, specification tables, form columns — carry their own
   maximum instead of relying on the container to supply one. */

.fx-page .container {
  max-width: 1720px;
  margin-inline: auto;
  padding-inline: var(--fx-pad);
}

/* Long-form reading. Guides and policies set their own line length, because
   a 1720px paragraph is unreadable at any type size. */
.fx-page .container--reading { max-width: 74ch; }

.fx-page { --fx-prose: 68ch; }

/* ----------------------------------------------------------------- rhythm --
   Section spacing was var(--space-20) — a fixed 80px that is too much on a
   phone and too little at 1920. One fluid step, and the modifiers keep
   working. */

.fx-page .section { padding-block: clamp(3rem, 5vw, 5.75rem); }
.fx-page .section--flush-top { padding-top: 0; }
.fx-page .section--tight { padding-block: clamp(2rem, 3.25vw, 3.25rem); }
.fx-page .section--tint { background: var(--fx-canvas-2); }
.fx-page .section--dark,
.fx-page .section--graphite { background: var(--fx-graphite); color: var(--fx-inv); }

/* ------------------------------------------------------------- typography --
   The legacy scale tops out well below the flagship display size, which is why
   every intermediary page read as a document rather than a product surface.

   These rules set SIZE, WEIGHT and TRACKING only. They deliberately set no
   colour.

   An earlier draft set `color: var(--fx-ink)` on every heading and
   `var(--fx-ink-2)` on every paragraph, and qa-screens.mjs immediately found
   fifteen pages of unreadable text: the quotation band, the distribution map
   panel, the 3D viewer and every worked formula are dark panels that set their
   own colour on a container, and an element rule carrying a class selector
   beats an inherited one. "Request a quotation." measured 1.04:1.

   Colour on a light surface is already correct from components.css, and each
   component below states its own where it has one. Nothing here reaches
   across a surface boundary. */

.fx-page main h1 {
  font-size: clamp(2.1rem, 3.5vw, 3.15rem);
  line-height: 1.1; letter-spacing: -0.028em; font-weight: 700; text-wrap: balance;
}
.fx-page main h2 {
  font-size: clamp(1.45rem, 1.9vw, 1.95rem);
  line-height: 1.18; letter-spacing: -0.02em; font-weight: 700;
}
.fx-page main h3 {
  font-size: clamp(1.05rem, 1.15vw, 1.2rem);
  line-height: 1.28; letter-spacing: -0.012em; font-weight: 600;
}
.fx-page main h4 { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.004em; }
.fx-page main .type-small { font-size: 0.875rem; }
.fx-page main .type-caption { font-size: 0.8125rem; }

.fx-page .eyebrow,
.fx-page .k-card__kicker,
.fx-page .dist-region__kicker {
  display: block; font-family: var(--font-mono); font-size: 0.6875rem;
  font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--fx-accent); margin-bottom: 0.9rem;
}

/* ------------------------------------------------------------ breadcrumbs --
   Quieter, and on the same gutter as everything else. */

.fx-page .breadcrumb { padding-block: 1.15rem 0; font-size: 0.8125rem; }
.fx-page .breadcrumb a,
.fx-page .breadcrumb li { color: var(--fx-ink-3); }
.fx-page .breadcrumb [aria-current="page"] { color: var(--fx-ink-2); font-weight: 500; }
.fx-page .breadcrumb li:not(:last-child)::after { color: #C6CBD3; }

/* ------------------------------------------------------- page introduction --
   Every intermediary page opened the same way: an H1 and a 640px paragraph
   against 800px of nothing. The introduction is now a two-column band — the
   statement on the left, and on the right whatever the page can say factually
   about its own scope. Where a page has nothing to put there the column is
   simply absent and the lead keeps its measure. */

.fx-intro { padding-block: clamp(2.25rem, 3.6vw, 4rem) clamp(2rem, 3.2vw, 3.25rem); }
.fx-intro__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
  gap: clamp(2rem, 5vw, 5.5rem); align-items: end;
}
.fx-intro__grid--wide { grid-template-columns: minmax(0, 1fr) minmax(0, 27rem); }
.fx-intro__lead {
  margin-top: 1.15rem; max-width: 46ch;
  font-size: clamp(1rem, 1.08vw, 1.125rem); line-height: 1.62; color: var(--fx-ink-2);
}
.fx-intro__aside { padding-bottom: 0.4rem; }
.fx-intro__facts { display: grid; gap: 0.85rem; margin: 0; }
.fx-intro__facts > div {
  display: flex; justify-content: space-between; gap: 1.5rem;
  padding-bottom: 0.85rem; border-bottom: 1px solid var(--fx-rule);
}
.fx-intro__facts dt { font-size: 0.8125rem; color: var(--fx-ink-3); }
.fx-intro__facts dd {
  margin: 0; font-family: var(--font-mono); font-size: 0.8125rem;
  font-weight: 600; color: var(--fx-ink); text-align: right; font-variant-numeric: tabular-nums;
}
.fx-intro__note { margin-top: 1rem; font-size: 0.75rem; line-height: 1.55; color: var(--fx-ink-3); }

/* ------------------------------------------------------------------ cards --
   The legacy card is a 1px box with a radius and 24px of padding, repeated
   until a page looks like a settings screen. The flagship card is a panel with
   one hairline and a top rule that appears on hover. */

.fx-page .card {
  background: var(--fx-canvas);
  border: none; border-radius: 0;
  box-shadow: inset 0 0 0 1px var(--fx-rule);
  padding: clamp(1.4rem, 1.9vw, 1.9rem);
  position: relative; transition: box-shadow .18s ease, transform .18s ease;
}
.fx-page .card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--fx-accent); opacity: 0; transition: opacity .18s ease;
}
.fx-page .card--link:hover {
  transform: none;
  box-shadow: inset 0 0 0 1px #CDD3DC, 0 10px 30px -18px rgba(12, 15, 20, .35);
}
.fx-page .card--link:hover::before { opacity: 1; }
.fx-page .card h3 { margin-bottom: .55rem; }
.fx-page .card p { color: var(--fx-ink-2); font-size: .875rem; line-height: 1.6; }
.fx-page .section--tint .card { background: var(--fx-canvas); }

/* Grids: a shared gutter, and a 12-column-friendly minimum so a three-up row
   never resolves to a 170px column with fourteen characters on a line. */
.fx-page .grid-2 { gap: clamp(2rem, 4vw, 4.5rem); align-items: start; }
.fx-page .grid-3,
.fx-page .grid-4 { gap: clamp(1rem, 1.6vw, 1.6rem); }

/* --------------------------------------------------------- family feature --
   The Z5 family page presented its one series as a single card in a
   three-column grid: a 380px box holding a schematic circle, with 900px of
   white beside it. A family with one series is not a grid, it is a feature —
   the photograph takes the larger column and the published range sits beside
   it as data rather than as a sentence. */

.fx-feature {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: stretch; box-shadow: inset 0 0 0 1px var(--fx-rule);
  background: var(--fx-canvas);
}
/* THE PANEL STOPS PAINTING A GROUND OF ITS OWN.
   It was --fx-canvas-2, a light grey half behind a photograph that covered it
   completely, so the colour never showed and never had to agree with anything.
   The family visual is master J now, whose studio sweep is warm, and the two
   together read as a pale pink block sitting inside the card. The card is already
   --fx-canvas, so the panel simply inherits it: one background, one white, and
   the tool on top of it. */
.fx-feature__media { position: relative; overflow: hidden; background: transparent; min-height: 100%; }
.fx-feature__media picture { display: block; height: 100%; }
/* CONTAIN, NOT COVER, AND THAT IS THE WHOLE COMPOSITION FIX.
   The panel takes its height from the copy column, so cover had to scale a 2.39
   photograph to fill a box nearer 1.3 and threw away most of its width -- which is
   why the frame it replaced rendered as a macro of the flute band. With contain the
   photograph is never cropped: it fits to the panel's width, letterboxes into
   space that is now the same white as the card, and the tool sits at a size a
   reader can actually read the geometry from.
   object-position goes to center with it. At 52% it was steering a crop that no
   longer exists. */
.fx-feature__img {
  display: block; width: 100%; height: 100%; min-height: 340px;
  object-fit: contain; object-position: center;
  /* Identical to the products-page lead, and for the identical reason: this is the
     same master and the same crop. saturate takes out the warm cast, contrast(1.18)
     takes the ground from #F3ECEC to effectively white so it stops being a shape at
     all, and it is held at 1.18 because only 0.5% of the tool sits above luminance
     240 -- past that it starts eating the shank's highlight. */
  filter: saturate(.5) brightness(1.03) contrast(1.21);
  /* HORIZONTAL ONLY, because only the horizontal edges are real here. Contain fits
     this photograph to the panel's width, so its left and right edges land exactly
     on the panel's -- a hairline from the card's own rule -- while its top and
     bottom now land on the panel's too, because the frame is cut to the panel's
     own ratio rather than fitted into it. Sized to THIS crop: the tool spans
     38.5%-61.3% vertically and runs from the left edge to 79.8% horizontally, so
     every fade stops clear of it. The vertical pair go all the way in to 30% and
     70% -- further than the products page, and deliberately. This frame is cut
     tall enough to fill the panel, which means it reaches into the corners of the
     sweep where the studio light has fallen off to luminance 223, and that is the
     one part of the ground the tone curve cannot get to white. Those bands carry
     nothing but falloff, so dissolving them costs no photograph and takes the last
     of the warm haze with them. 5% on the left stays the short one, for the reason
     the products page gives: the shank leaves the frame on that side and a
     generous fade would dissolve the product rather than its background. */
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0, #000 5%, #000 84%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 30%, #000 70%, transparent 100%);
  mask-image:
    linear-gradient(90deg, transparent 0, #000 5%, #000 84%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 30%, #000 70%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.fx-feature__body {
  padding: clamp(1.75rem, 3vw, 3.25rem);
  display: flex; flex-direction: column; gap: 1.1rem; justify-content: center;
}
.fx-feature__title { font-size: clamp(1.5rem, 2.1vw, 2.1rem) !important; letter-spacing: -.024em; }
.fx-feature__lead { font-size: .9375rem; line-height: 1.65; color: var(--fx-ink-2); max-width: 48ch; }
.fx-feature__facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .9rem 1.75rem; margin: .35rem 0 0; }
.fx-feature__facts > div { border-top: 1px solid var(--fx-rule); padding-top: .65rem; }
.fx-feature__facts dt { font-size: .75rem; color: var(--fx-ink-3); }
.fx-feature__facts dd {
  margin: .25rem 0 0; font-family: var(--font-mono); font-size: .9375rem;
  font-weight: 600; color: var(--fx-ink); font-variant-numeric: tabular-nums;
}
.fx-feature__actions { display: flex; flex-wrap: wrap; gap: .85rem 1.25rem; align-items: center; margin-top: .6rem; }
.fx-feature__note { font-size: .75rem; line-height: 1.55; color: var(--fx-ink-3); max-width: 52ch; }

/* An rgba background is a colour nothing can measure. The contrast check reads the
   computed value and has no parent to composite against, so rgba(11,64,150,.045)
   presents as #0B4096 and every sentence on the Israel routing panel reported at
   1.11:1. Tints are opaque here, and are the colour the compositor would have
   produced anyway.

/* ----------------------------------------------------------------- badges --
   Square, mono, quiet. A pill in a rounded tint reads as a web app. */

.fx-page .badge {
  border-radius: 0; font-family: var(--font-mono);
  font-size: .6875rem; letter-spacing: .1em; text-transform: uppercase;
  padding: .4rem .65rem; background: var(--fx-canvas-2); color: var(--fx-ink-2);
}
.fx-page .badge--accent { background: #ECF0F7; color: var(--fx-accent); }

/* ------------------------------------------------------------------ rules --
   One hairline colour site-wide. */

.fx-page main hr { border: none; border-top: 1px solid var(--fx-rule); margin-block: clamp(2rem, 3vw, 3rem); }

/* ------------------------------------------------------- technical tables --
   Desktop: a real specification table — tabular numerals, a heavy head rule,
   hairline rows, no vertical borders and no zebra fill.
   Mobile is handled further down, where the table becomes a record list. */

.fx-page .spec-table,
.fx-page .catalog-table,
.fx-page table.data-table {
  width: 100%; border-collapse: collapse; font-size: .875rem;
}
.fx-page .spec-table th,
.fx-page .catalog-table th,
.fx-page table.data-table th {
  text-align: left; font-family: var(--font-mono); font-size: .6875rem;
  font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
  color: var(--fx-ink-3); padding: 0 1rem .85rem 0;
  border-bottom: 2px solid var(--fx-ink); white-space: nowrap;
}
.fx-page .spec-table td,
.fx-page .catalog-table td,
.fx-page table.data-table td {
  padding: .95rem 1rem .95rem 0; border-bottom: 1px solid var(--fx-rule);
  color: var(--fx-ink); vertical-align: middle;
}
.fx-page .spec-table tbody tr:hover,
.fx-page .catalog-table tbody tr:hover { background: #FAFAFB; }
.fx-page .spec-table td:last-child,
.fx-page .catalog-table td:last-child { padding-right: 0; text-align: right; }
.fx-page .num,
.fx-page .catalog-table td:not(:first-child):not(:last-child) {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}

/* A table wider than its column scrolls inside its own box. The page never
   scrolls sideways to accommodate one. */
.fx-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ------------------------------------------------------------------ forms --
   Standardised: label, control height, focus ring, error and consent copy,
   plus a two-column grouping so a thirteen-field enquiry stops being 1,100px
   of vertical column with the page empty on both sides. */

.fx-page .form-field { margin-bottom: 0; }
.fx-page .form-field label,
.fx-page .fx-form__label {
  display: block; font-size: .8125rem; font-weight: 600; letter-spacing: -.003em;
  color: var(--fx-ink); margin-bottom: .45rem;
}
.fx-page .form-field .required { color: #A4262C; font-weight: 700; }
.fx-page .form-field .hint { display: block; font-size: .75rem; color: var(--fx-ink-3); margin-top: .4rem; line-height: 1.5; }
.fx-page .form-field input,
.fx-page .form-field select,
.fx-page .form-field textarea,
.fx-page .fx-field input,
.fx-page .fx-field select,
.fx-page .fx-field textarea {
  width: 100%; min-height: 48px; border-radius: 0;
  border: 1px solid #C3C9D2; background: var(--fx-canvas); color: var(--fx-ink);
  font-family: var(--font-sans); font-size: .9375rem; padding: .7rem .85rem;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.fx-page .form-field select { appearance: none; padding-right: 2.4rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--fx-ink-2) 50%), linear-gradient(135deg, var(--fx-ink-2) 50%, transparent 50%);
  background-position: calc(100% - 1.15rem) 55%, calc(100% - .85rem) 55%;
  background-size: 6px 6px, 6px 6px; background-repeat: no-repeat;
}
.fx-page .form-field input:hover,
.fx-page .form-field select:hover,
.fx-page .form-field textarea:hover { border-color: #9BA3AF; }
.fx-page .form-field input:focus,
.fx-page .form-field select:focus,
.fx-page .form-field textarea:focus {
  outline: none; border-color: var(--fx-accent);
  box-shadow: 0 0 0 3px rgba(11, 64, 150, .14);
}
.fx-page .form-field textarea { min-height: 128px; line-height: 1.6; }
.fx-page .form-field.has-error input,
.fx-page .form-field.has-error select,
.fx-page .form-field.has-error textarea { border-color: #A4262C; }
.fx-page .form-field .error-message,
.fx-page .form-checkbox .error-message { color: #A4262C; font-size: .75rem; margin-top: .4rem; }
.fx-page .form-field__unavailable {
  border-radius: 0; border: 1px dashed #C3C9D2; background: var(--fx-canvas-2);
  color: var(--fx-ink-3); font-size: .8125rem; padding: .85rem;
}

/* Grouped form layout. */
.fx-form { display: grid; gap: clamp(2rem, 3vw, 2.75rem); }
.fx-form__group { display: grid; gap: 1.1rem; border: none; padding: 0; margin: 0; min-width: 0; }
.fx-form__legend {
  display: block; width: 100%; padding: 0 0 .9rem; margin-bottom: .35rem;
  border-bottom: 1px solid var(--fx-rule);
  font-family: var(--font-mono); font-size: .6875rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--fx-ink-3);
}
.fx-form__row {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.1rem clamp(1rem, 2vw, 2rem);
}
.fx-form__row--single { grid-template-columns: minmax(0, 1fr); }
.fx-form__row > .fx-form__span { grid-column: 1 / -1; }
.fx-form__foot { display: grid; gap: 1.1rem; padding-top: .35rem; }
.fx-page .form-checkbox { align-items: flex-start; gap: 1.55rem; font-size: .8125rem; line-height: 1.6; color: var(--fx-ink-2); }
/* THE BOX ITSELF IS NOT RESTYLED. components.css gives it 24px plus 12px of padding on
   every side, so the hit area clears 44px without moving anything on screen. An earlier
   draft here set 20px and qa-screens.mjs reported it on the quotation form and the
   distributor enquiry — the two forms where that box is the control a visitor MUST hit
   to submit. Only the accent colour is ours. */
.fx-page .form-checkbox input[type="checkbox"],
.fx-page .form-checkbox input[type="radio"] { accent-color: var(--fx-accent); }
.fx-form__note { font-size: .75rem; line-height: 1.6; color: var(--fx-ink-3); max-width: 60ch; }

/* The form column itself, and the panel it sits in. */
.fx-formwrap { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: clamp(2.5rem, 5vw, 5.5rem); align-items: start; }
.fx-formwrap__aside { position: sticky; top: 2rem; display: grid; gap: 1.5rem; }
.fx-formpanel { background: var(--fx-canvas); box-shadow: inset 0 0 0 1px var(--fx-rule); padding: clamp(1.5rem, 3vw, 2.75rem); }

/* --------------------------------------------------------------- notices ---
   .alert was a rounded tinted box. Square, with a rule on the leading edge. */

.fx-page .alert {
  border-radius: 0; background: var(--fx-canvas-2); color: var(--fx-ink-2);
  border-left: 3px solid var(--fx-rule); padding: 1.15rem 1.35rem; font-size: .875rem; line-height: 1.6;
}
.fx-page .alert--info { border-left-color: var(--fx-accent); background: #F4F6FA; }
.fx-page .alert--error { border-left-color: #A4262C; background: #FBF4F5; color: #7C1D22; }
.fx-page .alert--success { border-left-color: #1E6B44; background: #F4F8F6; color: #175536; }

/* --------------------------------------------------------- the quiet band --
   Pages ended on a centred question and an outlined button floating in a tint
   field — the weakest thing on the site, and it appeared five times. It is now
   a left-aligned rule-and-link row that reads as navigation, so it stops
   competing with the pre-footer quotation band directly beneath it. */

.fx-nextstep { padding-block: clamp(2.25rem, 3.5vw, 3.5rem); border-top: 1px solid var(--fx-rule); }
.fx-nextstep__inner { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 1rem 2.5rem; }
.fx-nextstep__text { max-width: 54ch; }
.fx-nextstep h2 { font-size: clamp(1.05rem, 1.2vw, 1.25rem) !important; letter-spacing: -.012em; }
.fx-nextstep p { margin-top: .4rem; font-size: .875rem; color: var(--fx-ink-3); }
.fx-nextstep__links { display: flex; flex-wrap: wrap; gap: 1.75rem; }
.fx-nextstep__links a {
  font-size: .875rem; font-weight: 600; color: var(--fx-accent);
  border-bottom: 1px solid rgba(11, 64, 150, .3); padding-bottom: .15rem;
}
.fx-nextstep__links a:hover { border-bottom-color: var(--fx-accent); text-decoration: none; }

/* -------------------------------------------------- restrained 3D pointer --
   The full interactive viewer lives on the family page, once, against the one
   variant whose model was actually measured. A size page gets a link. */

.fx-3dlink {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.75rem;
  background: var(--fx-graphite); color: var(--fx-inv);
  padding: clamp(1.15rem, 1.8vw, 1.6rem) clamp(1.25rem, 2vw, 1.85rem);
  border-top: 2px solid var(--fx-accent);
}
.fx-3dlink__text { flex: 1 1 22rem; min-width: 0; }
.fx-3dlink__title { font-size: .9375rem; font-weight: 600; color: var(--fx-inv); }
.fx-3dlink__note { margin-top: .35rem; font-size: .8125rem; line-height: 1.55; color: var(--fx-inv-2) !important; }
.fx-3dlink__cta {
  flex: none; font-size: .875rem; font-weight: 600; color: var(--fx-inv) !important;
  border-bottom: 1px solid rgba(255, 255, 255, .35); padding-bottom: .2rem;
}
.fx-3dlink__cta:hover { border-bottom-color: var(--fx-accent-bright); color: var(--fx-accent-bright) !important; text-decoration: none; }

/* ------------------------------------------------------------ dark panels --
   Any link inside a graphite panel. The global link colour is #082C68, which
   measures 1.38:1 on #12141A — the defect Sprint 4 found on ten pages. Stated
   here once so it cannot recur in a new panel. */

/* A dark CONTAINER must state its own colour, not leave its children to state
   theirs. flagship.css sets `.fx-page h1, h2, h3 { color: inherit }`, so a
   panel that paints a dark background and then colours only its title hands
   that title whatever the page inherits — which is the page's ink. That is how
   "Where coverage is developing" came to be #0C0F14 on #12141A. Every dark
   surface on the site is named here. */
.fx-page .fx-ondark,
.fx-page .fx-3dlink,
.fx-page .fx-map,
.fx-page .map-panel,
.fx-page .pk-viewer,
.fx-page .section--graphite { color: var(--fx-inv); }
.fx-page .fx-ondark a,
.fx-page .fx-map a,
.fx-page .section--graphite a:not(.btn) { color: var(--fx-accent-bright); }
.fx-page .fx-ondark p,
.fx-page .fx-map p,
.fx-page .section--graphite p { color: var(--fx-inv-2); }
.fx-page .fx-ondark h2,
.fx-page .fx-ondark h3,
.fx-page .fx-map h2,
.fx-page .section--graphite h2,
.fx-page .section--graphite h3 { color: var(--fx-inv); }

/* ============================================================== RESPONSIVE */

@media (max-width: 1279px) {
  .fx-formwrap { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
}

@media (max-width: 1023px) {
  .fx-intro__grid,
  .fx-intro__grid--wide { grid-template-columns: minmax(0, 1fr); gap: 2rem; align-items: start; }
  .fx-intro__aside { padding-bottom: 0; max-width: 34rem; }
  .fx-formwrap { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
  .fx-formwrap__aside { position: static; order: -1; }
  .fx-page .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 767px) {
  /* Below 768 a three-up or four-up grid resolves to columns too narrow to set
     a sentence in. Sprint 4 found paragraphs rendering 113px wide with zero
     pixels of overflow; the rule is one column, stated for every grid. */
  .fx-page .grid-2,
  .fx-page .grid-3,
  .fx-page .grid-4 { grid-template-columns: minmax(0, 1fr); }
  .fx-form__row { grid-template-columns: minmax(0, 1fr); }
  .fx-nextstep__inner { flex-direction: column; align-items: flex-start; }
  .fx-nextstep__links { gap: 1rem 1.5rem; }
  .fx-3dlink { flex-direction: column; align-items: flex-start; }
  .fx-page main h1 { font-size: clamp(1.8rem, 7.5vw, 2.3rem); }
  .fx-intro { padding-block: 1.75rem 1.5rem; }
  .fx-formpanel { padding: 1.25rem; box-shadow: none; }

  /* Specification tables become record lists. A six-column technical table at
     390px is either unreadable or a sideways scroll, and neither is a design.
     The header cell is carried in as a data-label so nothing is lost. */
  .fx-page .spec-table--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .fx-page .spec-table--stack tbody tr {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .1rem 1rem;
    padding: 1rem 0; border-bottom: 1px solid var(--fx-rule);
  }
  .fx-page .spec-table--stack td {
    display: flex; justify-content: space-between; gap: 1rem;
    grid-column: 1 / -1; padding: .3rem 0; border: none; font-size: .875rem;
  }
  .fx-page .spec-table--stack td::before {
    content: attr(data-label); font-family: var(--font-sans);
    font-size: .8125rem; color: var(--fx-ink-3); font-weight: 400;
  }
  .fx-page .spec-table--stack td:first-child {
    font-weight: 700; font-size: 1rem; padding-bottom: .55rem;
  }
  .fx-page .spec-table--stack td:first-child::before { content: none; }
  .fx-page .spec-table--stack td:last-child { text-align: left; padding-top: .7rem; }
  .fx-page .spec-table--stack td:last-child::before { content: none; }
  .fx-page .spec-table--stack td:last-child .btn { width: 100%; }
}

/* ============================================== family feature, responsive */

@media (max-width: 1023px) {
  .fx-feature { grid-template-columns: minmax(0, 1fr); }
  .fx-feature__img { min-height: 0; aspect-ratio: 16 / 9; height: auto; }
}

@media (max-width: 767px) {
  /* THE PANEL SHAPE IS WHAT SETS THE TOOL SIZE ON A PHONE, not a width or a
     transform. The frame is 1.12 and `contain` fits it to whichever side runs out
     first: in a 16/9 box that is the height, so the photograph came out 221px wide
     inside a 350px box and the tool measured 177px with pillars of white either
     side of it. 7/5 is still wider than the frame, so the fit is still by height and
     nothing is cropped -- there is simply less letterboxing to pay for. Measured on
     a 390 render: 177px to 224px, +27%. 4/3 was the next step and overshot at +34%.
     ONLY below 768. The two-column card returns at 1024, and the 768-1023 tablet
     case keeps the 16/9 it was approved with. */
  .fx-feature__img { aspect-ratio: 7 / 5; }
  .fx-feature__facts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem 1rem; }
  .fx-feature__body { padding: 1.35rem 1.25rem 1.75rem; }
  .fx-feature__actions .btn { width: 100%; justify-content: center; }
}

/* Inline text action beside a primary button. */
.fx-feature__inline,
.fx-inline-action {
  font-size: .875rem; font-weight: 600; color: var(--fx-accent);
  border-bottom: 1px solid rgba(11, 64, 150, .3); padding-bottom: .15rem;
}
.fx-feature__inline:hover,
.fx-inline-action:hover { border-bottom-color: var(--fx-accent); text-decoration: none; }

/* A badge inside a flex column stretches to the column width unless told not
   to; a full-width status pill reads as a banner, not a status. */
.fx-feature__body > .badge,
.fx-page .fx-stack > .badge { align-self: flex-start; }

/* ===================================================== Z5 FAMILY PAGE ==== */

/* ------------------------------------------------------------ family hero --
   Full-bleed, like the approved homepage hero: the photograph is sized in
   viewport units and meets the copy panel with no frame, no radius and no
   caption box floating over it. */

.fx-famhero {
  display: grid; grid-template-columns: minmax(0, 46fr) minmax(0, 54fr);
  align-items: stretch; background: var(--fx-canvas); border-bottom: 1px solid var(--fx-rule);
}
.fx-famhero__copy {
  display: flex; flex-direction: column; align-items: flex-start; justify-content: center;
  gap: 1.15rem;
  padding: clamp(2.25rem, 4vw, 4.75rem) clamp(2rem, 4vw, 4.5rem) clamp(2.25rem, 4vw, 4.75rem) var(--fx-pad);
}
.fx-famhero__title {
  font-size: clamp(2.2rem, 3.6vw, 3.5rem) !important;
  line-height: 1.08; letter-spacing: -0.03em; font-weight: 700;
}
.fx-famhero__lead { font-size: clamp(.9375rem, 1.02vw, 1.0625rem); line-height: 1.62; color: var(--fx-ink-2); max-width: 44ch; }
.fx-famhero__facts {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem 2.25rem; margin: .5rem 0 0; width: 100%; max-width: 30rem;
}
.fx-famhero__facts > div { border-top: 1px solid var(--fx-rule); padding-top: .7rem; }
.fx-famhero__facts dt { font-size: .75rem; color: var(--fx-ink-3); }
.fx-famhero__facts dd {
  margin: .3rem 0 0; font-family: var(--font-mono); font-size: 1rem; font-weight: 600;
  color: var(--fx-ink); font-variant-numeric: tabular-nums;
}
.fx-famhero__actions { display: flex; flex-wrap: wrap; align-items: center; gap: .85rem 1.5rem; margin-top: .75rem; }
/* THE PANEL SIZES ITSELF FROM THE PHOTOGRAPH, NOT FROM THE COPY BESIDE IT.
   `align-self: start` is the whole correction. Stretched, this panel took its height
   from the copy column — 832px at 1440, 816 at 1200 — against a width of 54vw, so a
   landscape photograph sat in a PORTRAIT box and object-fit: cover threw away 38% of it
   at 1440 and 47% at 1200. Every attempt to fix that by choosing a different photograph
   or a cleverer object-position was treating the symptom: no landscape image fits a
   portrait hole.
   With `align-self: start` the panel stops stretching and `aspect-ratio: 3/2` gives it
   the source's own shape, so cover has nothing left to trim. The copy column is now
   taller than the photograph and that is correct — the two were never the same thing,
   and equalising them was what broke the picture. */
.fx-famhero__media {
  position: relative; background: var(--fx-canvas-2); overflow: hidden;
  align-self: start;
  aspect-ratio: 3 / 2;
}
.fx-famhero__media picture { display: block; height: 100%; }
/* OBJECT-POSITION IS BACK TO CENTER, AND THAT IS THE POINT.
   It was 10% to rescue a 38% crop — anchoring the window on the cutting head so the
   crop ate the empty shank instead of the tool. With the panel at the source's own
   ratio the crop is 0.03%, there is nothing to rescue, and the composition on screen is
   the composition the photographer framed.
   `min-height` is gone with it. The panel sets the height now; a floor on the IMAGE
   could only ever exceed its container and quietly reintroduce a crop. */
.fx-famhero__img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; }
.fx-famhero__caption {
  position: absolute; left: 0; right: 0; bottom: 0;
  margin: 0; padding: 2.5rem 1.25rem .85rem;
  font-size: .6875rem; line-height: 1.5; color: rgba(255, 255, 255, .92) !important;
  background: linear-gradient(to top, rgba(8, 11, 16, .72), rgba(8, 11, 16, 0));
}

/* ---------------------------------------------------------- family points --
   Four statements, numbered. Not four cards: a card implies a destination and
   these go nowhere. */

.fx-points { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(1.5rem, 2.5vw, 2.75rem); }
.fx-point { display: grid; gap: .5rem; align-content: start; border-top: 2px solid var(--fx-ink); padding-top: 1rem; }
.fx-point__idx { font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .14em; color: var(--fx-accent); }
.fx-point h3 { margin: 0; }
.fx-point p { margin: 0; font-size: .875rem; line-height: 1.62; color: var(--fx-ink-2); }

/* ----------------------------------------------------------- section head --
   A heading on the left, the qualifying sentence on the right — so a section
   can carry its provenance without a 74ch paragraph under every H2. */

.fx-sechead {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 34rem);
  gap: 1rem clamp(2rem, 5vw, 5rem); align-items: end;
  padding-bottom: 1.5rem; margin-bottom: clamp(1.75rem, 2.5vw, 2.5rem);
  border-bottom: 1px solid var(--fx-rule);
}
.fx-sechead__note { font-size: .875rem; line-height: 1.62; color: var(--fx-ink-3); margin: 0; }

/* ------------------------------------------------------ 3D module (Z5) ---
   A COMPACT TECHNICAL MODULE. It was a band, and it behaved like one.

   FIRST ATTEMPT, AND WHY IT WAS NOT ENOUGH. The canvas was cut from 641px to 440 and
   capped at 1180 wide, which was right and which did not fix the complaint: the module
   was still 1000px at 1440, more than a 900px viewport, so a reader arriving at it saw
   a 3D viewer and read it as the point of the page. The canvas was never most of the
   height. The heading block, a three-line paragraph, a separate dark activation strip
   with its own sentence, and an open provenance panel carrying six measured values were.

   SO THE WHOLE MODULE IS THE UNIT NOW, not the canvas inside it. Identification and one
   line of description on the left at about a third; the viewer on the right; the
   provenance closed into a disclosure; the activation reduced to a button on the
   preview. Roughly 380px at 1440 against 1000, with the viewer still 653x280 —
   inspectable, and plainly a supporting feature.

   --fx-model-h is the VIEWER height and is read by both the poster box and the live
   canvas, so activating the model cannot change the height of anything. It is an
   explicit length rather than an aspect ratio, because an aspect ratio ties the height
   to the width and that is what made a wide screen produce a tall viewer. */
.fx-modelband {
  --fx-model-h: 280px;
  background: var(--fx-graphite-deep); color: var(--fx-inv);
  border-top: 2px solid var(--fx-accent);
  padding-block: clamp(1.25rem, 1.8vw, 1.625rem);
}
@media (max-width: 1023px) { .fx-modelband { --fx-model-h: 270px; } }

/* THE CAP IS ON THE MODULE, NOT JUST THE VIEWER. Capping only the viewer left the
   heading and the provenance running the full container, so 1920 still produced a
   wider, emptier module. One measure, centred, and the composition is the same shape
   at 1920 as at 1280. */
/* ITS OWN GUTTER, NOT --fx-pad. The editorial gutter is 72px at 1440 and 96px at 1920 —
   right for a full-bleed band, wrong for a measure that is already capped. It took 192px
   out of a 1060px module at 1920 and left the viewer 530px wide. The cap IS the measure;
   the gutter only has to clear the screen edge. */
.fx-modelband__inner {
  width: 100%; max-width: 1100px; margin-inline: auto;
  padding-inline: clamp(1.25rem, 2.4vw, 1.75rem);
}
.fx-modelband__grid {
  display: grid;
  grid-template-columns: minmax(0, 36fr) minmax(0, 64fr);
  gap: clamp(1.5rem, 2.6vw, 2.5rem);
  /* Centred, not top-aligned. The intro runs about 150px against the viewer's 324, and
     top-aligning them left roughly 170px of empty graphite under the link — the
     "excessive empty dark space" the review named. Centred, the two columns read as one
     object instead of as a short column standing beside a tall one. */
  align-items: center;
}
.fx-modelband__intro { min-width: 0; }
/* Not a section heading. It identifies WHICH model is on screen, which is a caption's
   job done at h2 because the page runs h1 -> h2 and a skipped level is a defect. */
.fx-modelband h2 {
  margin: .3rem 0 0; color: var(--fx-inv);
  font-size: clamp(1.0625rem, 1.25vw, 1.25rem) !important;
  line-height: 1.25; letter-spacing: -.015em;
}
.fx-modelband__lead {
  margin: .6rem 0 0; font-size: .8125rem; line-height: 1.6;
  color: var(--fx-inv-2) !important; max-width: 34ch;
}
.fx-modelband__spec {
  display: inline-block; margin-top: .85rem;
  font-size: .8125rem; font-weight: 600; color: var(--fx-accent-bright) !important;
  border-bottom: 1px solid rgba(91, 144, 232, .35); padding-bottom: .15rem;
}
.fx-modelband__spec:hover { border-bottom-color: var(--fx-accent-bright); text-decoration: none; }
.fx-modelband__panel { min-width: 0; }
.fx-modelband .pk-viewer { margin: 0; }

/* THE PROVENANCE, CLOSED. Same words, published, and no longer the tallest thing in the
   module. Closed is not hidden: it is in the markup, in the accessibility tree and found
   by in-page search, and <details> needs no JavaScript to open. */
.fx-modelband__more { border-top: 1px solid rgba(255, 255, 255, .1); }
.fx-modelband__more > summary {
  cursor: pointer; list-style: none;
  padding: .6rem clamp(.85rem, 1.4vw, 1.15rem);
  font-size: .75rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--fx-inv-2);
  display: flex; align-items: center; gap: .5rem; min-height: 40px;
}
.fx-modelband__more > summary::-webkit-details-marker { display: none; }
.fx-modelband__more > summary::after {
  content: "+"; margin-left: auto; font-size: 1rem; font-weight: 400; line-height: 1;
}
.fx-modelband__more[open] > summary::after { content: "–"; }
.fx-modelband__more > summary:hover { color: var(--fx-inv); }
.fx-modelband__more > summary:focus-visible { outline: 2px solid var(--fx-accent-bright); outline-offset: -2px; }
.fx-modelband__moreBody { padding: 0 clamp(.85rem, 1.4vw, 1.15rem) clamp(.85rem, 1.4vw, 1.15rem); }

@media (max-width: 899px) {
  /* Stacked. The intro is short enough that it reads as a caption above the viewer
     rather than as a section of its own. */
  .fx-modelband__grid { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .fx-modelband__lead { max-width: none; }
}

/* --------------------------------------------------------- range selector --
   Standard against extended, side by side, so the difference between the two
   length classes is visible rather than inferred from an OAL column. */

.fx-rangesel { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1.5rem, 3vw, 3.5rem); }
.fx-rangesel__col { min-width: 0; }
.fx-rangesel__title { margin: 0 0 .3rem; }
.fx-rangesel__note { margin: 0 0 1rem; font-size: .8125rem; color: var(--fx-ink-3); font-family: var(--font-mono); }
.fx-rangesel__list { list-style: none; margin: 0; padding: 0; display: grid; }
.fx-rangesel__item {
  display: grid; grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr) auto;
  align-items: baseline; gap: 1rem; padding: .85rem 0; min-height: 48px;
  border-bottom: 1px solid var(--fx-rule); color: inherit;
}
.fx-rangesel__item:hover { text-decoration: none; }
.fx-rangesel__item:hover .fx-rangesel__d { color: var(--fx-accent); }
.fx-rangesel__item:hover .fx-rangesel__go { color: var(--fx-accent); transform: translateX(3px); }
.fx-rangesel__d { font-family: var(--font-mono); font-size: .9375rem; font-weight: 600; color: var(--fx-ink); }
.fx-rangesel__code { font-family: var(--font-mono); font-size: .8125rem; color: var(--fx-ink-3); }
.fx-rangesel__go { color: #B9BFC8; transition: transform .16s ease, color .16s ease; }

.fx-rangetable { margin-top: clamp(2.5rem, 4vw, 4rem); }
.fx-rangetable__title { margin-bottom: 1.1rem; }
.fx-rangetable__note { margin-top: 1rem; font-size: .8125rem; line-height: 1.6; color: var(--fx-ink-3); max-width: 90ch; }

/* --------------------------------------------------- engineering overview --
   Published against pending, stated as two lists rather than as prose. */

.fx-fieldgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1.5rem, 3vw, 3.25rem); }
.fx-fieldset h3 { margin: 0 0 .9rem; }
.fx-fieldset p { font-size: .875rem; line-height: 1.62; color: var(--fx-ink-2); margin: 0 0 .85rem; }
.fx-fieldlist { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
.fx-fieldlist li {
  position: relative; padding: .55rem 0 .55rem 1.35rem;
  border-bottom: 1px solid var(--fx-rule);
  font-size: .875rem; line-height: 1.55; color: var(--fx-ink-2);
}
.fx-fieldlist li::before { content: ""; position: absolute; left: 0; top: .95rem; width: 6px; height: 6px; }
.fx-fieldlist--yes li::before { background: var(--fx-accent); }
.fx-fieldlist--no li::before { box-shadow: inset 0 0 0 1px #A9B0BA; }

/* ================================================ family page, responsive */

@media (max-width: 1279px) {
  .fx-points { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* THE FAMILY HERO STACKS UNTIL 1200px, NOT 1024px, AND THE REASON IS MEASURED.
   The two-column hero stretches the media panel to the height of the COPY column, so
   the image box is (54vw wide) x (however tall the copy runs) — and the copy runs about
   800px whatever the viewport. At 1024 that is a 553x788 panel: aspect 0.70, a portrait
   slot holding a landscape photograph, with object-fit: cover throwing away 53% of the
   frame's width and magnifying what was left by 2.1x. It was the worst render on the
   site and it was invisible, because nothing renders a hero at 1024 and looks at it.
   Below 1200 the hero stacks and the image takes its own 3:2 shape instead. */
@media (max-width: 1199px) {
  .fx-famhero { grid-template-columns: minmax(0, 1fr); }
  .fx-famhero__copy { padding: 2rem var(--fx-pad) 2.25rem; }
  .fx-famhero__media { order: -1; }
  /* 3:2 — the source ratio, and the rule behind it has not changed: where the panel
     takes the image its own shape, object-fit: cover performs no crop at all and the
     cutting edges cannot be trimmed. WAS 5:2, for the 2560x1024 band. The hero is now
     the uncropped master frame at 1536x1025, so the panel follows it to 3:2 — which on
     a 390px phone is a 260px-tall photograph of a whole tool instead of a 156px strip
     across its middle. Asserted by tests/productImagery.test.js. */
  .fx-famhero__img { min-height: 0; aspect-ratio: 3 / 2; height: auto; }
}

@media (max-width: 1023px) {
  .fx-sechead,
  .fx-modelband__head { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .fx-fieldgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fx-fieldgrid > :last-child { grid-column: 1 / -1; }
}

@media (max-width: 767px) {
  .fx-points,
  .fx-rangesel,
  .fx-fieldgrid { grid-template-columns: minmax(0, 1fr); }
  .fx-fieldgrid > :last-child { grid-column: auto; }
  .fx-famhero__facts { max-width: none; gap: .85rem 1.25rem; }
  .fx-famhero__actions .btn { width: 100%; justify-content: center; }
  .fx-rangesel__item { grid-template-columns: minmax(0, 1fr) auto; row-gap: .2rem; }
  .fx-rangesel__code { grid-column: 1 / 2; }
  .fx-rangesel__go { grid-row: 1 / 3; grid-column: 2; align-self: center; }
}

/* The caption sits with the copy, not over the photograph. As an overlay it was
   white text whose readability depended on a gradient the contrast check cannot
   see — and if the image fails it was white on #F4F2F1. It is also a scrim over
   approved product photography, which this site does not do. */
.fx-famhero__caption {
  margin: .35rem 0 0; font-size: .75rem; line-height: 1.55;
  color: var(--fx-ink-3) !important; max-width: 46ch;
  background: none; position: static; padding: 0;
}

/* --------------------------------------------------- the 3D viewport frame --
   The renderer clears to rgb(244,245,247) and the poster is rendered on the same field,
   so the interactive model and the static one match exactly. That is correct and must
   not change — but a light plate dropped into a graphite module with no frame reads as
   a hole in the page. Framed: a hairline, a cobalt top rule, and the disclosure below
   sharing the same edge. A CAD viewport inside a technical environment. */

.fx-page .fx-modelband .pk-viewer {
  border: none; border-radius: 0; padding: 0; text-align: left;
  background: var(--fx-graphite); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
}
/* THE STAGE IS THE DECLARED HEIGHT, NOT A RATIO OF THE WIDTH. `aspect-ratio: 2 / 1` in
   knowledge.css is the general seat's rule and stays true wherever that seat is used
   plain; here the height is fixed so the poster box and the live canvas are the same
   box, and so a wider browser does not buy a taller viewer. */
.fx-page .fx-modelband .pk-viewer__stage {
  margin: 0; box-shadow: inset 0 0 0 1px rgba(12, 15, 20, .12);
  border-top: 2px solid var(--fx-accent);
  aspect-ratio: auto; height: var(--fx-model-h);
}
/* Live, the stage also carries the control bar and the hint beneath the canvas, so it
   is the CANVAS that takes the declared height and the stage grows around it by the
   height of its own chrome — the same shape the poster had, plus the controls. */
.fx-page .fx-modelband .pk-viewer[data-model-state="live"] .pk-viewer__stage,
.fx-page .fx-modelband .pk-viewer[data-model-state="loading"] .pk-viewer__stage,
.fx-page .fx-modelband .pk-viewer[data-model-state="error"] .pk-viewer__stage { height: auto; }
.fx-page .fx-modelband .pk-viewer[data-model-state="live"] .pk-viewer__canvas { height: var(--fx-model-h); }
/* The loading and error panels sit in the same box rather than collapsing it, so
   activating the model never moves the page under the reader's thumb. */
.fx-page .fx-modelband .pk-viewer__status {
  min-height: var(--fx-model-h);
  display: flex; align-items: center; justify-content: center;
  padding-block: 0;
}
/* THE POSTER IS 1200x600 AND THE BOX NO LONGER IS.
   The box is wider than 2:1 at every width the two-column layout produces, so `cover`
   scales the poster to the width and crops top and bottom — which removes exactly the
   empty ground above and below the tool and nothing else. Stacked below 900px the box
   narrows towards 2:1 and `cover` would start taking the ends off the tool instead, so
   `contain` there: the renderer clears to rgb(244,245,247) and this stage is #f4f5f7,
   so the letterbox is the same colour as the plate and reads as nothing at all. */
.fx-page .fx-modelband .pk-viewer__poster { object-fit: cover; object-position: center; }
@media (max-width: 899px) {
  .fx-page .fx-modelband .pk-viewer__poster { object-fit: contain; object-position: center 40%; }
}

/* ACTIVATION IS A BUTTON, NOT A BAND. It was a full-width dark strip with a gradient,
   a primary button and a sentence about rotating and zooming — roughly 130px of chrome
   under a preview that already showed the tool, on a module that was too tall. The
   button sits on the preview now and the weight is one short line beside it. */
.fx-page .fx-modelband .pk-viewer__activate {
  position: absolute; inset-inline: 0; bottom: 0;
  flex-direction: row; align-items: center; justify-content: center; gap: .75rem;
  padding: .7rem .9rem;
  background-color: rgba(18, 20, 26, .82);
  background-image: linear-gradient(to top, rgba(18, 20, 26, .3), rgba(18, 20, 26, 0) 70%);
}
.fx-page .fx-modelband .pk-viewer__activate .btn {
  padding: .5rem 1.1rem; font-size: .8125rem; min-height: 40px;
}
.fx-page .fx-modelband .pk-viewer__activate .pk-viewer__hint { text-align: left; font-size: .75rem; }
/* The no-JavaScript state: the same strip, carrying the weight instead of the button. */
.fx-page .fx-modelband .pk-viewer__stage > .pk-viewer__hint[data-model-activate] {
  padding: .7rem .9rem; font-size: .75rem;
}

/* Controls stay chrome: one row of small pills, not a toolbar. */
.fx-page .fx-modelband .pk-viewer__controls { gap: .3rem; padding: .4rem .55rem; }
.fx-page .fx-modelband .pk-viewer__control {
  font-size: .6875rem; letter-spacing: .04em; padding: .35rem .7rem; min-height: 32px;
}
/* The live hint joins the dark chrome instead of sitting on the light plate.
   knowledge.css gives it `background: none`, so it inherited the stage's #f4f5f7 while
   this module's override was forcing the text to --fx-inv-2 — pale grey on near-white,
   which is a contrast failure and reads as an unfinished strip under the controls.
   Canvas, controls and hint are one chrome band now. */
.fx-page .fx-modelband .pk-viewer[data-model-state="live"] .pk-viewer__hint {
  padding: .4rem .55rem; font-size: .6875rem;
  background: var(--color-neutral-900);
}

.fx-page .fx-modelband .pk-viewer__meta { padding: 0; background: transparent; }
.fx-page .fx-modelband .pk-viewer__note { color: var(--fx-inv-2) !important; font-size: .75rem; line-height: 1.6; max-width: 92ch; }
.fx-page .fx-modelband .pk-viewer__note strong { color: var(--fx-inv); }
.fx-page .fx-modelband .pk-viewer__note--quiet { font-size: .6875rem; }
.fx-page .fx-modelband .pk-viewer__hint { color: var(--fx-inv-2) !important; }
.fx-page .fx-modelband .pk-viewer__verified { gap: .2rem 1rem; margin-top: .75rem; }
.fx-page .fx-modelband .pk-viewer__verified strong { font-size: .8125rem; }

@media (max-width: 639px) {
  /* Six pills in a 350px column wrapped onto three rows — a toolbar taller than it had
     any right to be. Two rows here, and every target still clears 44px. */
  .fx-page .fx-modelband .pk-viewer__controls { gap: .375rem; padding: .5rem .625rem; }
  .fx-page .fx-modelband .pk-viewer__control { font-size: .6875rem; padding: .5rem .75rem; min-height: 44px; }
  .fx-page .fx-modelband .pk-viewer__activate { flex-direction: column; gap: .4rem; }
  .fx-page .fx-modelband .pk-viewer__activate .pk-viewer__hint { text-align: center; }
}

/* ================================================== PRODUCT PAGE (PDP) === */

/* The two lines the flagship prototype did not have: what the tool is, and how a
   machinist writes it. The dimension code is the string a buyer searches for and
   quotes back in an enquiry, and it was nowhere above the fold. */

.fx-pdp__sub { margin: .5rem 0 0; font-size: .9375rem; color: var(--fx-ink-2); }
.fx-pdp__code {
  margin: .75rem 0 0; font-family: var(--font-mono); font-size: 1rem; font-weight: 600;
  letter-spacing: .01em; color: var(--fx-ink); font-variant-numeric: tabular-nums;
}
.fx-page .fx-pdp__status {
  margin-top: .85rem; display: inline-flex; align-items: center; gap: .55rem;
  font-family: var(--font-mono); font-size: .6875rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--fx-accent);
}
.fx-page .fx-pdp__status::before { content: ""; width: 6px; height: 6px; background: var(--fx-accent); flex: none; }

/* Barcode, given the weight of an identifier rather than of a footnote. It is
   labelled Barcode because that is what it is: not an SKU, not a part number,
   not a GTIN. */
.fx-page .fx-pdp__sku {
  margin-top: 1.35rem; padding-top: 1.1rem; border-top: 1px solid var(--fx-rule);
  font-size: .8125rem; color: var(--fx-ink-3);
}
.fx-page .fx-pdp__sku strong {
  font-family: var(--font-mono); font-size: .9375rem; color: var(--fx-ink);
  font-variant-numeric: tabular-nums; margin-left: .5rem;
}

/* The specification band on a legacy container. The prototype used .fx-shell;
   every page now uses .container, which resolves to the same measure. */
.fx-page .fx-specwrap .fx-specgrid { align-items: start; }
.fx-page .fx-spec th { width: 46%; }

/* The section index. Eleven links in a row is a navigation bar, not a list of
   inline links separated by whitespace. */
.fx-page .pdp-index { border-block: 1px solid var(--fx-rule); background: var(--fx-canvas); }
.fx-page .pdp-index__list {
  display: flex; flex-wrap: wrap; gap: .1rem 1.75rem; list-style: none; margin: 0; padding: .9rem 0;
  counter-reset: none;
}
.fx-page .pdp-index__list a {
  font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--fx-ink-3); padding: .45rem 0; display: inline-block;
}
.fx-page .pdp-index__list a:hover { color: var(--fx-accent); text-decoration: none; }

/* Technical references strip. */
.fx-page .tech-refs {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(1rem, 2vw, 2.25rem);
}
.fx-page .tech-refs li { border-top: 2px solid var(--fx-ink); padding-top: .9rem; }
.fx-page .tech-refs li a { font-size: .9375rem; font-weight: 600; color: var(--fx-ink); }
.fx-page .tech-refs li a:hover { color: var(--fx-accent); text-decoration: none; }
.fx-page .tech-refs li p { margin: .35rem 0 0; font-size: .8125rem; line-height: 1.55; color: var(--fx-ink-3); }

@media (max-width: 1023px) {
  .fx-page .tech-refs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 767px) {
  .fx-page .tech-refs { grid-template-columns: minmax(0, 1fr); }
  .fx-page .pdp-index__list { gap: 0 1.1rem; }
  .fx-pdp__code { font-size: .9375rem; }
}

/* Nine siblings in one column is 500px of list. Two columns on desktop. */
.fx-page #related-tools .fx-rangesel__list {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(1.5rem, 3vw, 3.5rem);
}
@media (max-width: 767px) {
  .fx-page #related-tools .fx-rangesel__list { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------- table action buttons ----
   `.btn--tertiary` carries `min-height: auto`, and inside a table it is padded
   to 4px. That was a 26px-tall control before the flagship layer reached these
   pages, because the button inherited a 1.5 line-height; `.fx-page .btn` sets
   1.1, which took it to 23px — below the 24px floor, on every Add-to-RFQ
   button in both catalogue tables. Found by qa-screens.mjs at 390.

   A real floor, and the whole row is the target on a phone. */
.fx-page table.data .btn--tertiary,
.fx-page .catalog-table .btn--tertiary {
  min-height: 32px; padding-block: .45rem; align-items: center;
}
@media (max-width: 767px) {
  .fx-page table.data .btn--tertiary,
  .fx-page .catalog-table .btn--tertiary {
    min-height: 44px; width: 100%; justify-content: center;
  }
}

/* ================================================= GLOBAL DISTRIBUTION === */

/* ------------------------------------------------------------- the map ----
   The map was the weakest thing on the site. Three reasons, all fixable
   without claiming one fact more than before:

     · the emphasised cells were a small fill inside a contrasting stroke, so
       two hundred of them read as rings — the "ooooo" effect that makes a
       schematic map look like clip art;
     · the regions were marked with a 10px-radius rounded rectangle, which is
       the visual language of a selected form control;
     · the land was #1E2531 with a #2C3547 outline on a near-black panel, so
       the world itself read as noise rather than as a continent shape.

   Redrawn as a plotting chart: solid dots, a legible land tone, cobalt only
   where the emphasis is, and drawing-frame register ticks instead of a box. */

.fx-page .map-panel {
  background: var(--fx-graphite-deep);
  color: var(--fx-inv);
  border-top: 2px solid var(--fx-accent);
  padding-block: clamp(2.5rem, 4.5vw, 4.5rem);
}
.fx-page .map-panel__inner { max-width: 1720px; margin-inline: auto; padding-inline: var(--fx-pad); }
.fx-page .map-panel__head {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 24rem);
  gap: 1.25rem clamp(2rem, 5vw, 5rem); align-items: end;
}
.fx-page .map-panel__title { color: var(--fx-inv); font-size: clamp(1.5rem, 2.1vw, 2.1rem); letter-spacing: -.022em; }
.fx-page .map-panel__lead { color: var(--fx-inv-2) !important; max-width: 56ch; font-size: .9375rem; line-height: 1.62; }

.fx-page .map-figure { margin-top: clamp(1.75rem, 3vw, 2.75rem); }
.fx-page .map-figure__scroll { overflow-x: visible; }
.fx-page .map-figure svg { display: block; width: 100%; min-width: 0; height: auto; }

/* Land: legible, quiet, and one tone. No outline — an outline on a 2px dot is
   a ring. */
.fx-page .map-land { fill: #3A465B; stroke: none; }
.fx-page .map-land--focus { fill: #3C7BE0; stroke: none; }

.fx-page .map-graticule { stroke: rgba(255, 255, 255, .05); stroke-width: .6; fill: none; }

/* The register marks. A drawing frame states an extent; it does not enclose. */
.fx-page .map-tick { fill: none; stroke: var(--fx-accent-bright); stroke-width: 1.4; opacity: .9; }
.fx-page .map-extent { fill: none; stroke: rgba(91, 144, 232, .22); stroke-width: .7; }

/* Callout leaders, as on a technical sheet. */
.fx-page .map-leader { fill: none; stroke: rgba(91, 144, 232, .45); stroke-width: .7; }
.fx-page .map-leader__node { fill: var(--fx-accent-bright); }
.fx-page .map-label {
  fill: rgba(255, 255, 255, .88); font-family: var(--font-mono);
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
}
.fx-page .map-label--muted { fill: rgba(255, 255, 255, .4); }

.fx-page .map-legend {
  margin-top: clamp(1.5rem, 2.5vw, 2.25rem); padding: 1.25rem 0 0;
  border-top: 1px solid var(--fx-rule-dark);
  display: flex; flex-wrap: wrap; gap: .75rem 2.5rem; list-style: none;
}
.fx-page .map-legend li { display: flex; align-items: center; gap: .65rem; font-size: .75rem; color: var(--fx-inv-2); }
.fx-page .map-legend__swatch { width: 10px; height: 10px; border-radius: 50%; border: none; flex: none; }
.fx-page .map-legend__swatch--focus { background: #3C7BE0; }
.fx-page .map-note { margin-top: 1rem; font-size: .75rem; line-height: 1.6; color: rgba(255, 255, 255, .45) !important; max-width: 92ch; }

/* --------------------------------------------------------- region panels --
   Three panels of unequal height with a left-edge accent, the middle one
   visually heavier than the others for no reason a reader could name. Equal
   panels, one rule across the top of each, and the emphasis carried by the
   rule's colour rather than by a border on one side. */

.fx-page .dist-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1.25rem, 2.5vw, 2.5rem); align-items: stretch; }
.fx-page .dist-region {
  border: none; border-radius: 0; border-top: 2px solid var(--fx-rule);
  background: transparent; padding: 1.15rem 0 0; display: flex; flex-direction: column; gap: .65rem;
}
.fx-page .dist-region--emphasis { border-top-color: var(--fx-accent); }
.fx-page .dist-region h3 { margin: 0; }
.fx-page .dist-region p { margin: 0; font-size: .875rem; line-height: 1.62; color: var(--fx-ink-2); }
.fx-page .dist-region__kicker { margin-bottom: .1rem; }
.fx-page .section--tint .dist-region { border-top-color: #D8DCE2; }

/* The Israel / TSC block. Unchanged in substance — this is a presentation
   change only, and the routing copy is read from handoffRegions.js. */
.fx-page .dist-israel,
.fx-page .alert--info.dist-note {
  margin-top: clamp(2rem, 3vw, 3rem);
  border-left: 3px solid var(--fx-accent);
  background: #F4F6FA;
  padding: clamp(1.15rem, 2vw, 1.6rem) clamp(1.25rem, 2.2vw, 1.85rem);
  max-width: 78ch;
}

/* ------------------------------------------------- what is not claimed ----
   A wall of bullets in a 50%-width column with 800px of nothing beside it.
   Two columns, and each claim reads as a stated limit rather than as a list
   item. */

.fx-page .dist-claims { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .1rem clamp(2rem, 4vw, 4rem); list-style: none; margin: 0; padding: 0; }
.fx-page .dist-claims li {
  position: relative; padding: .85rem 0 .85rem 1.75rem;
  border-bottom: 1px solid var(--fx-rule);
  font-size: .875rem; line-height: 1.6; color: var(--fx-ink-2);
}
.fx-page .dist-claims li::before {
  content: "—"; position: absolute; left: 0; top: .85rem;
  color: var(--fx-ink-3); font-family: var(--font-mono);
}

@media (max-width: 1023px) {
  .fx-page .map-panel__head { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .fx-page .dist-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fx-page .dist-claims { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 767px) {
  /* The in-map callouts stop being readable well before the map does: at 350px
     of rendered width a 10px label in a 732-unit viewBox is under 5px. The
     legend below the map carries the same information at a readable size, so
     the labels are dropped rather than shrunk into illegibility. */
  .fx-page .map-label { display: none; }
  .fx-page .map-leader,
  .fx-page .map-leader__node { display: none; }
  .fx-page .map-land { r: 2.9; }
  .fx-page .map-land--focus { r: 4; }
  .fx-page .map-legend { gap: .75rem 1.5rem; }
  .fx-page .dist-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------------- the form aside ----
   A long enquiry with nothing beside it reads as an obligation. The column
   states what happens to the enquiry, which is the question a distributor
   actually has before filling one in. */

.fx-asidecard { border-top: 2px solid var(--fx-ink); padding-top: 1.1rem; }
.fx-asidecard__note { margin: 0 0 .85rem; font-size: .8125rem; line-height: 1.65; color: var(--fx-ink-3); }
.fx-asidecard__note:last-child { margin-bottom: 0; }
.fx-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
.fx-steps li { display: grid; grid-template-columns: 2.25rem minmax(0, 1fr); gap: .75rem; padding: .7rem 0; border-bottom: 1px solid var(--fx-rule); }
.fx-steps li:last-child { border-bottom: none; }
.fx-steps__n { font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .12em; color: var(--fx-accent); padding-top: .15rem; }
.fx-steps p { margin: 0; font-size: .8125rem; line-height: 1.6; color: var(--fx-ink-2); }

/* ------------------------------------------------------ what you get/ask ---
   Two cards of unequal height, the right one starting fifteen pixels lower
   than the left because each sized itself to its own content. Two columns on
   one rule, stretched to a shared height. */

.fx-terms { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1.5rem, 3vw, 3.5rem); align-items: stretch; }
.fx-term { border-top: 2px solid var(--fx-ink); padding-top: 1.15rem; }
.fx-term h3 { margin: 0 0 .9rem; }
.fx-term ul { list-style: none; margin: 0; padding: 0; }
.fx-term li {
  position: relative; padding: .7rem 0 .7rem 1.5rem;
  border-bottom: 1px solid var(--fx-rule);
  font-size: .875rem; line-height: 1.6; color: var(--fx-ink-2);
}
.fx-term li::before {
  content: ""; position: absolute; left: 0; top: 1.15rem;
  width: 6px; height: 6px; background: var(--fx-accent);
}
.fx-term > p { margin: 1rem 0 0; font-size: .75rem; line-height: 1.6; color: var(--fx-ink-3); }

@media (max-width: 767px) {
  .fx-terms { grid-template-columns: minmax(0, 1fr); }
}

/* ================================================== THE CATALOGUE FINDER == */

/* Two paragraphs of dense explanatory prose stood between the heading and the
   controls — 260 words about which facets exist and why four of them render
   nothing. Every word of it is true and worth keeping, and none of it is what
   somebody looking for a 12mm end mill needs first. The facts move into a
   disclosure; the controls come first. */

.fx-explain {
  margin-bottom: clamp(1.5rem, 2.5vw, 2rem);
  border-bottom: 1px solid var(--fx-rule);
}
.fx-explain > summary {
  cursor: pointer; list-style: none; padding: .85rem 0;
  font-size: .8125rem; font-weight: 600; color: var(--fx-ink-2);
  display: flex; align-items: center; gap: .65rem; min-height: 44px;
}
.fx-explain > summary::-webkit-details-marker { display: none; }
.fx-explain > summary::before {
  content: "+"; font-family: var(--font-mono); font-size: .875rem;
  color: var(--fx-accent); width: 1rem; flex: none;
}
.fx-explain[open] > summary::before { content: "−"; }
.fx-explain > summary:hover { color: var(--fx-ink); }
.fx-explain__body { padding: 0 0 1.25rem 1.65rem; display: grid; gap: .85rem; }
.fx-explain__body p { margin: 0; font-size: .8125rem; line-height: 1.65; color: var(--fx-ink-3); max-width: 96ch; }

/* The control row. `.cluster` is a flex wrap, so six controls of six natural
   widths made a ragged line that read as an admin form. A grid gives them one
   rhythm, and search takes the wider column because a query is longer than a
   facet value. */
.fx-page .cluster:has(#catalog-search) {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(5, minmax(0, 1fr));
  gap: 1rem clamp(.85rem, 1.2vw, 1.25rem);
  align-items: end;
  padding: clamp(1.15rem, 1.8vw, 1.6rem);
  background: var(--fx-canvas);
  box-shadow: inset 0 0 0 1px var(--fx-rule);
}
.fx-page .cluster:has(#catalog-search) .form-field { min-width: 0 !important; }

.fx-page .filter-active { display: flex; flex-wrap: wrap; align-items: center; gap: .65rem 1rem; }
.fx-page .filter-active__list { display: flex; flex-wrap: wrap; gap: .5rem; }
.fx-page .filter-active__list button,
.fx-page .filter-chip {
  font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .06em;
  border-radius: 0; background: #ECF0F7; color: var(--fx-accent); border: none;
  padding: .45rem .7rem; min-height: 32px;
}
.fx-page #catalog-clear-filters { min-height: 32px; padding-block: .45rem; }

/* The result count, given the weight of a result count. */
.fx-page .catalog-count,
.fx-page [data-catalog-count] { font-family: var(--font-mono); font-size: .75rem; color: var(--fx-ink-3); letter-spacing: .04em; }

/* ------------------------------------------------------------ comparison --
   The comparison panel is a real engineering feature and was styled like a
   dialog. It reads as a specification sheet now: a heavy head rule, tabular
   numerals, and the differing values marked. */

.fx-page .cmp-panel,
.fx-page #compare-panel {
  background: var(--fx-canvas); box-shadow: inset 0 0 0 1px var(--fx-rule);
  border-radius: 0; padding: clamp(1.25rem, 2vw, 1.85rem);
}
.fx-page #compare-panel table { width: 100%; border-collapse: collapse; font-size: .875rem; }
.fx-page #compare-panel th { text-align: left; font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .1em; text-transform: uppercase; color: var(--fx-ink-3); padding: 0 1rem .8rem 0; border-bottom: 2px solid var(--fx-ink); }
.fx-page #compare-panel td { padding: .85rem 1rem .85rem 0; border-bottom: 1px solid var(--fx-rule); font-variant-numeric: tabular-nums; }
/* The comparison checkbox is a touch target in a table row. 20px is under the
   24px floor and it is the control that opens the whole comparison feature, so
   it gets the same treatment the consent checkbox has: a real box, with padding
   carrying the hit area past 44px and negative margin keeping the layout. */
.fx-page .cmp-check {
  width: 24px; height: 24px; accent-color: var(--fx-accent);
  padding: 11px; box-sizing: content-box; margin: -11px;
}

@media (max-width: 1279px) {
  .fx-page .cluster:has(#catalog-search) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .fx-page .cluster:has(#catalog-search) .form-field:first-child { grid-column: 1 / -1; }
}

@media (max-width: 767px) {
  .fx-page .cluster:has(#catalog-search) { grid-template-columns: minmax(0, 1fr); padding: 1rem; }
  .fx-page .cluster:has(#catalog-search) .form-field:first-child { grid-column: auto; }
  .fx-explain__body { padding-left: 0; }
}

/* ------------------------------------------------------- family cards -----
   The frame centres its content in a 180px flex box, so a 1280x617 photograph
   sat at its own height with grey above and below it while the two schematic
   cards filled theirs. One ratio for all three, and the photograph fills it. */
.fx-page .family-card__frame { aspect-ratio: 16 / 9; min-height: 0; padding: 0; }
.fx-page .family-card__frame svg { max-width: 110px; }
.fx-page .family-card__frame--photo picture,
.fx-page .family-card__frame--photo { display: block; }
.fx-page .family-card__img { height: 100%; }

/* A facet select must fit its own longest option. "All shank diameters" was
   clipped to "All shank diamete" at 1440. */
.fx-page .cluster:has(#catalog-search) select { font-size: .875rem; padding-right: 2rem; }

/* =================================================== TECHNICAL CENTER ==== */

/* POLISH, NOT REWRITE. The content architecture is the strongest thing on the
   site and nothing here touches a guide, a formula or a provenance banner.
   What changes is presentation: the hub head opened with an unstyled browser
   search control, the section intros were grey afterthoughts, and the cards
   were bordered boxes repeated nine times with the same eyebrow on each. */

.fx-page .k-hubhead { padding-block: clamp(2.25rem, 3.6vw, 4rem) clamp(2rem, 3.2vw, 3rem); }
/* EXPLICIT ROWS, and no row gap. An earlier version let the search span four
   implicit rows; the rows then stretched to fill its height and the head opened
   with 80px of nothing between the heading and its own lead. */
.fx-page .k-hubhead .container {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
  column-gap: clamp(2rem, 5vw, 5.5rem); row-gap: 0; align-content: start;
}
.fx-page .k-hubhead .k-eyebrow { grid-column: 1; grid-row: 1; }
.fx-page .k-hubhead h1 { grid-column: 1; grid-row: 2; }
.fx-page .k-hubhead__lead { grid-column: 1; grid-row: 3; max-width: 46ch; font-size: clamp(1rem, 1.08vw, 1.125rem); line-height: 1.62; }
.fx-page .k-hubhead__meta { grid-column: 1; grid-row: 4; max-width: 60ch; margin-top: 1.25rem; }
.fx-page .k-hubhead .k-search { grid-column: 2; grid-row: 1 / 4; align-self: start; max-width: none; margin-top: .35rem; }

/* The search control. It was a default browser input — a 1px hairline at the
   user-agent height — introducing the largest body of content on the site. */
.fx-page .k-search input {
  width: 100%; min-height: 52px; border-radius: 0;
  border: 1px solid #C3C9D2; background: var(--fx-canvas);
  font-family: var(--font-sans); font-size: .9375rem; padding: .8rem 1rem .8rem 2.6rem;
  transition: border-color .15s ease, box-shadow .15s ease;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23616875' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.5-3.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: .95rem center; background-size: 17px 17px;
}
.fx-page .k-search input:focus {
  outline: none; border-color: var(--fx-accent);
  box-shadow: 0 0 0 3px rgba(11, 64, 150, .14);
}
.fx-page .k-search__list { border-radius: 0; border-color: var(--fx-rule); }
.fx-page .k-search__list li { border-bottom-color: var(--fx-rule); }

/* Section groups: a heading with its intro beside it, not a grey line beneath. */
.fx-page .k-hubgroup { padding-block: clamp(2rem, 3.5vw, 3.25rem) 0; }
.fx-page .k-hubgroup__title { font-size: clamp(1.25rem, 1.6vw, 1.6rem) !important; letter-spacing: -.018em; }
.fx-page .k-hubgroup__lead {
  margin-top: .45rem; max-width: 74ch;
  font-size: .9375rem; line-height: 1.62; color: var(--fx-ink-3);
  padding-bottom: 1.15rem; border-bottom: 1px solid var(--fx-rule); margin-bottom: 1.5rem;
}

/* Cards: one hairline, a rule that answers on hover, and the eyebrow demoted —
   "General reference" appearing nine times in accent type is nine claims
   competing with nine titles. */
.fx-page .k-card {
  border: none; border-radius: 0; box-shadow: inset 0 0 0 1px var(--fx-rule);
  padding: clamp(1.15rem, 1.6vw, 1.5rem); position: relative;
  transition: box-shadow .18s ease;
}
.fx-page .k-card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--fx-accent); opacity: 0; transition: opacity .18s ease;
}
.fx-page .k-card:hover { box-shadow: inset 0 0 0 1px #CDD3DC, 0 10px 30px -18px rgba(12, 15, 20, .35); }
.fx-page .k-card:hover::before { opacity: 1; }
.fx-page .k-card__kicker { color: var(--fx-ink-3) !important; margin-bottom: .55rem; }
.fx-page .k-card__title { font-size: 1rem; letter-spacing: -.01em; }
.fx-page .k-card__note { font-size: .8125rem; line-height: 1.6; color: var(--fx-ink-3); }
.fx-page .k-card__cta { margin-top: auto; padding-top: .9rem; font-size: .8125rem; font-weight: 600; color: var(--fx-accent); }

/* A guide is long-form. It gets a reading measure, and the page around it is
   the same width as every other page. */
.fx-page .k-article,
.fx-page .k-guide__body { max-width: 74ch; }
.fx-page .k-article p,
.fx-page .k-guide__body p { line-height: 1.72; }

@media (max-width: 1023px) {
  .fx-page .k-hubhead .container { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
  .fx-page .k-hubhead .k-search { grid-column: 1; grid-row: auto; max-width: 34rem; }
  .fx-page .k-hubhead__meta { grid-column: 1; }
}

/* ------------------------------------------------------------------- 404 --
   The code was set at 7rem in --color-neutral-100 on white: 1.14:1, which is a
   watermark rather than a piece of information, and qa-screens.mjs reported it
   as unreadable text because that is what it is. A status code is a technical
   fact — it is set like one, in mono, at label scale, where it is legible. */
.fx-page .notfound { padding-block: clamp(3rem, 6vw, 6rem); }
.fx-page .notfound__code {
  font-size: .6875rem; font-weight: 600; letter-spacing: .16em;
  text-transform: uppercase; color: var(--fx-accent); margin-bottom: .9rem;
}
.fx-page .notfound__code::before { content: "HTTP "; }
.fx-page .notfound__routes,
.fx-page .notfound__routes li { border-color: var(--fx-rule); }
.fx-page .notfound__routes a { color: var(--fx-ink); }
.fx-page .notfound__routes span { color: var(--fx-ink-3); }
.fx-page .notfound__subhead { color: var(--fx-ink-3); margin-top: clamp(2.5rem, 4vw, 3.5rem); }

/* ------------------------------------------------------- outline buttons --
   `.fx-page .btn` sets `border: 0`, which is right for the solid buttons the
   flagship uses and wrong for every outline one: `.btn--secondary` and
   `.btn--tertiary` state only a border-COLOUR, so with the width zeroed they
   rendered as bare text. "Contact Us" on the About page, "View published
   specifications" in the Technical Center, and the Add-to-RFQ control in both
   catalogue tables were all buttons with nothing drawn around them.

   A rule, at the flagship's weight, square, with no radius. */
.fx-page .btn--secondary,
.fx-page .btn--outline {
  border: 1px solid #C3C9D2; background: transparent; color: var(--fx-ink);
}
.fx-page .btn--secondary:hover,
.fx-page .btn--outline:hover { border-color: var(--fx-ink); background: var(--fx-canvas-2); }
.fx-page table.data .btn--tertiary,
.fx-page .catalog-table .btn--tertiary { border: 1px solid #C3C9D2; color: var(--fx-accent); }
.fx-page table.data .btn--tertiary:hover,
.fx-page .catalog-table .btn--tertiary:hover { border-color: var(--fx-accent); background: #F4F6FA; }

/* A reading page. The measure lives on the content, never on the section — a
   section carries the page gutter, and setting a max-width on both centred a
   490px column inside a 1440px viewport with 470px of nothing either side. */
/* LEFT, not centred. Every other page on the site starts its content at the
   page gutter, in line with the logo and the breadcrumb above it. A centred
   reading column is a fine editorial pattern and the wrong one here: it put the
   About page 435px to the right of the breadcrumb that introduces it. */
.fx-page .container--reading {
  /* The gutter is padding INSIDE the box, so a bare 74ch max-width is 74ch minus two
     gutters of actual text — 448px at 1440, which is where the privacy policy was
     setting its prose and clipping its four-column table. The measure is stated on the
     content and the gutter added to it. */
  max-width: calc(74ch + 2 * var(--fx-pad));
  margin-inline: 0;
}
.fx-page .container--reading h2 { margin-top: clamp(2.25rem, 3.5vw, 3rem); }
.fx-page .container--reading p { line-height: 1.72; }
.fx-page .container--reading > .btn { margin-top: clamp(1.75rem, 3vw, 2.5rem); }

/* -------------------------------------------------------- policy pages ----
   A table may be wider than the prose around it. Four columns squeezed into a
   reading measure clipped the "why it is asked for" column on the privacy
   policy; the table takes the room it needs and scrolls inside its own box if
   the screen cannot give it. */
.fx-page .policy-table__scroll,
.fx-page .policy-section > .policy-table { max-width: none; }
.fx-page .policy-table { border-collapse: collapse; font-size: .875rem; }
/* COLUMN headers are mono and uppercase. ROW headers are not — a <th scope="row">
   is a label in a sentence, and the rule below used to set every one of them in
   mono caps, bottom-aligned against top-aligned cells beside them. */
.fx-page .policy-table thead th {
  text-align: left; font-family: var(--font-mono); font-size: .6875rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--fx-ink-3);
  padding: 0 1.25rem .8rem 0; border-bottom: 2px solid var(--fx-ink); vertical-align: bottom;
}
.fx-page .policy-table tbody th {
  text-align: left; font-weight: 600; color: var(--fx-ink);
  padding: .9rem 1.25rem .9rem 0; border-bottom: 1px solid var(--fx-rule);
  vertical-align: top; line-height: 1.6;
}
.fx-page .policy-table td { padding: .9rem 1.25rem .9rem 0; border-bottom: 1px solid var(--fx-rule); vertical-align: top; line-height: 1.6; }
.fx-page .policy-table tbody tr:nth-child(even) { background: transparent; }
.fx-page .policy-pending {
  border-radius: 0; border: none; border-left: 3px solid #B07A15;
  background: #FDF8EE; padding: clamp(1.25rem, 2vw, 1.75rem);
}
.fx-page .policy-pending__title { font-size: 1rem; }
.fx-page .policy__updated { font-family: var(--font-mono); font-size: .6875rem; letter-spacing: .12em; text-transform: uppercase; color: var(--fx-ink-3); }
.fx-page .policy__intro { font-size: clamp(1rem, 1.08vw, 1.0625rem); line-height: 1.65; color: var(--fx-ink-2); max-width: 60ch; }
.fx-page .policy-section > h2 { margin-top: clamp(2.25rem, 3.5vw, 3rem); }

/* A block-level submit is right on a phone and wrong at 1440, where it made a
   750px-wide button for a four-field form. It fills its column up to the width
   a button stops reading as a banner. */
.fx-page form .btn--block { width: 100%; max-width: 22rem; }
@media (max-width: 767px) { .fx-page form .btn--block { max-width: none; } }

/* The quotation form's own column, and the panel beside it. */
.fx-page .rfq-layout { gap: clamp(2.5rem, 5vw, 5.5rem); }
.fx-page .rfq-sidebar { display: grid; gap: 1.5rem; }
.fx-page .rfq-sidebar > div,
.fx-page .rfq-sidebar > aside {
  border-radius: 0; border: none; box-shadow: inset 0 0 0 1px var(--fx-rule);
  padding: clamp(1.15rem, 1.8vw, 1.6rem); background: var(--fx-canvas);
}

/* ------------------------------------------------- 1024 is not a desktop ---
   Both of these collapsed at 1023px, which leaves 1024 — a real, common
   viewport — running the full desktop column count. qa-screens.mjs measured
   the result: four-up technical references at 173px and three-up family cards
   at 198px, each setting a real sentence about twenty characters to a line,
   with zero pixels of overflow and nothing else to notice them.

   Sprint 4 found the same class of defect in the flagship grids and fixed it
   at the wrong breakpoint; this is the rest of it. */
@media (max-width: 1279px) {
  .fx-page .tech-refs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fx-page .family-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: none; }
  .fx-page .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 767px) {
  .fx-page .family-grid { grid-template-columns: minmax(0, 1fr); max-width: 480px; margin-inline: auto; }
}

/* SOURCE ORDER MATTERS. The 1279 block above was appended after the 767 one
   that already collapsed these to a single column, and at equal specificity
   the later rule wins — so a phone went back to two columns and the harness
   measured 122px paragraphs. The narrow case is restated last, which is where
   it has to be. */
@media (max-width: 767px) {
  .fx-page .tech-refs,
  .fx-page .grid-4,
  .fx-page .grid-3,
  .fx-page .grid-2 { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------- anchors and the header --
   The site header is sticky, so every in-page jump — the product page's own
   section index, the series page's "Interactive 3D model →", the catalogue's
   comparison panel — landed its target underneath it. Found by looking at a
   screenshot of the comparison opening: the column headings were behind the
   logo. One declaration, on everything that can be a jump target. */
.fx-page [id] { scroll-margin-top: 108px; }
@media (max-width: 1023px) { .fx-page [id] { scroll-margin-top: 88px; } }

/* ============================================================================
   LOCAL SUPPORT NOTICE — the RFQ success panel's TSC block, and the consent
   control that offers to forward the enquiry.

   Quieter than the commerce module's .fx-handoff on purpose. That panel was the
   WHOLE answer to an Israeli enquiry and carried the weight of one; this sits
   underneath an acceptance message and an RFQ reference, and is secondary to
   both. No border box and no fill — one rule above it, the way a footnote is set
   apart from the paragraph it belongs to.

   It lives here rather than in commerce.css because /rfq/ loads flagship.css and
   system.css and does not load commerce.css. Written in commerce.css these rules
   were simply inert.
   ========================================================================== */

.fx-page .fx-localroute[hidden],
.fx-page .rfq-local[hidden] { display: none; }

.fx-page .fx-localroute {
  border-top: 1px solid var(--fx-rule, var(--color-neutral-300, #E4E7EC));
  padding-top: 1.25rem;
  max-width: 44rem;
}

/* The element is an h2 because the page runs h1 → h2 and a skipped level is an
   accessibility defect. It is not a section heading though, so it is set at body
   scale — hence the class, which has to out-specify base.css's bare `h2`. */
.fx-page .fx-localroute__heading {
  margin: 0 0 0.4rem;
  font-size: 1rem;
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.fx-page .fx-localroute__message {
  margin: 0 0 1rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  max-width: 60ch;
}

/* ==========================================================================
   THE QUALITY STANDARD BAND — /company/about/, 2026-09-16.

   The section it replaces was a heading and one grey sentence saying nothing
   was confirmed. Something is confirmed now, and a claim about manufacturing
   standard that sits in body copy between two other h2s reads as a footnote
   rather than as a position.

   So it is a band: graphite, full-bleed, cobalt rule above it — the same
   environment as .fx-modelband and .fx-face, because it is the same KIND of
   statement and the site should not invent a third visual language for it.

   RESTRAINT IS THE POINT. No badge, no seal, no certificate graphic, no stock
   photograph of a factory floor. A claim this site cannot hand you a document
   for does not get illustrated as though it could. What carries it is
   typography and space, which is also what makes it read as premium rather
   than as marketing.

   Navy, graphite, white and cobalt only — all four are existing tokens.
   ========================================================================== */

.fx-quality {
  background: var(--fx-graphite-deep);
  color: var(--fx-inv);
  border-top: 2px solid var(--fx-accent);
  padding-block: clamp(3rem, 6vw, 6rem);
}

/* TWO COLUMNS, BOTH MEASURED. The headline is the position and the paragraph is
   the evidence, so they sit beside each other rather than stacking into a
   column of text. `justify-content: start` keeps the pair together instead of
   letting a 1920 screen push them to opposite edges — the same reasoning as the
   centred measure inside .fx-face. */
.fx-quality__grid {
  display: grid;
  grid-template-columns: minmax(0, 34rem) minmax(0, 34rem);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
  justify-content: start;
}

/* The approved headline is set in capitals, in the markup and not by a
   text-transform, so the published string IS the approved string — a
   transform would leave the DOM carrying wording nobody signed off.
   Capitals need air between letters and less between lines; both are here, and
   the size stops well short of a display face because this is an argument, not
   a hero. */
.fx-quality__headline {
  margin: 0.7rem 0 0;
  color: var(--fx-inv) !important;
  font-size: clamp(1.5rem, 2.6vw, 2.25rem) !important;
  line-height: 1.12;
  letter-spacing: 0.005em;
  /* THE BREAK FALLS BETWEEN THE TWO SENTENCES, and it is measured rather than
     hoped for. At the rendered size the first sentence is 23.7ch wide and the
     first sentence plus the next word is 27.3ch, so a 25ch measure can only
     break in one place. `ch` rather than `px`, because the size is fluid below
     1385px and a pixel measure would stop being the right one immediately.
     `text-wrap: balance` was here and is deliberately gone: balancing two lines
     of unequal length pulls a word back across the full stop, and that is the
     one break this headline cannot have — the two sentences ARE the rhetoric. */
  max-width: 25ch;
}

.fx-quality__rule {
  display: block; width: 64px; height: 3px; flex: none;
  background: var(--fx-accent-bright);
  margin-top: clamp(1.25rem, 2vw, 1.75rem);
}

.fx-quality__text {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.68;
  color: var(--fx-inv-2) !important;
}

/* The closing line is the one that has to land, so it is the only thing in the
   band set in full white at weight 600 apart from the headline. */
.fx-quality__close {
  margin: clamp(1.5rem, 2.5vw, 2rem) 0 0;
  font-size: clamp(1.0625rem, 1.4vw, 1.1875rem);
  font-weight: 600;
  line-height: 1.45;
  color: var(--fx-inv) !important;
  letter-spacing: -0.01em;
}

/* WHOSE CERTIFICATE IT IS, and where the document is not. Quiet, small, and
   below the rule — it qualifies the claim without arguing with it, which is
   exactly the weight a caveat should carry when the caveat is true. */
.fx-quality__doc {
  margin: clamp(2.25rem, 4vw, 3.25rem) 0 0;
  padding-top: clamp(1.25rem, 2vw, 1.75rem);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  font-size: 0.8125rem;
  line-height: 1.62;
  color: var(--fx-inv-2) !important;
  max-width: 82ch;
}
.fx-quality__doc a { color: var(--fx-accent-bright) !important; }
.fx-quality__doc a:hover { color: var(--fx-inv) !important; }

@media (max-width: 1023px) {
  /* Stacked, the headline keeps its own measure rather than running the full
     width of a tablet, which would leave a two-line statement reading as one
     very long line and lose the shape of it entirely. */
  .fx-quality__grid { grid-template-columns: minmax(0, 1fr); gap: clamp(1.75rem, 4vw, 2.5rem); }
  .fx-quality__headline { max-width: 20ch; }
}

@media (max-width: 639px) {
  .fx-quality__headline { max-width: none; }
}


/* --------------------------------------------------------- cutting data --
   Section 15 of the product architecture: two nine-column tables of
   manufacturer speeds and feeds, one per operation.

   THE TABLE SCROLLS, THE PAGE DOES NOT. Nine columns will not fit a 390px
   screen at a legible size, and the two remedies that do not work are
   shrinking the type until the numbers are unreadable and stacking 162 cells
   into a record list nobody can compare across. The table keeps a min-width
   and scrolls inside .fx-table-scroll, which the responsive harness already
   recognises and exempts.

   UNITS LIVE IN THE HEADER, under the symbol they belong to, so a value is
   never separated from its unit and never repeated thirty-six times. */

.fx-page .fx-cut {
  display: grid;
  gap: clamp(2.25rem, 4vw, 3.25rem);
  margin-top: clamp(1.75rem, 3vw, 2.5rem);
}

/* MIN-WIDTH: 0, AND IT IS LOAD-BEARING. A grid item defaults to min-width:auto,
   which means it refuses to be narrower than its own content — and its content
   is a table carrying a min-width. So the table's 46rem passed straight through
   the scroll box and set the width of the grid item, pushing the page 6px wide
   at 768. The scroll box could not scroll, because nothing was ever too big for
   it: the item had grown to fit instead. Without these two lines the overflow
   rule above them is advice rather than behaviour. */
.fx-page .fx-cut__op { min-width: 0; }
.fx-page .fx-cut__scroll { min-width: 0; }
.fx-page .fx-cut__intro {
  max-width: 74ch;
  margin-top: var(--space-3, 1rem);
  color: var(--fx-ink-2);
}
.fx-page .fx-cut__optitle {
  font-family: var(--font-mono);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--fx-ink);
  margin: 0;
}
.fx-page .fx-cut__opnote {
  margin: .45rem 0 1rem;
  font-size: .8125rem;
  color: var(--fx-ink-3);
  max-width: 68ch;
}
.fx-page .fx-cut__scroll { border-top: 2px solid var(--fx-ink); }
.fx-page .fx-cut__scroll:focus-visible { outline: 2px solid var(--fx-accent); outline-offset: 3px; }

.fx-page .fx-cut__table {
  width: 100%;
  min-width: 46rem;
  border-collapse: collapse;
  font-size: .8125rem;
}
.fx-page .fx-cut__table th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--fx-ink-3);
  padding: .8rem .75rem .7rem 0;
  border-bottom: 1px solid var(--fx-rule);
  vertical-align: bottom;
  white-space: nowrap;
}
/* NO UPPERCASING ON THE SYMBOLS. The column labels are set in small caps like every
   other table head on this site, and that rule turned fz into FZ, ae into AE and n
   into N. In cutting-tool notation the case IS part of the symbol, so the symbols
   opt out of the transform while the words beside them keep it. */
.fx-page .fx-cut__table th abbr {
  text-decoration: none; border: 0; color: var(--fx-ink);
  text-transform: none; font-size: .8125rem; letter-spacing: 0;
}
.fx-page .fx-cut__table td {
  padding: .8rem .75rem;
  padding-left: 0;
  border-bottom: 1px solid var(--fx-rule);
  color: var(--fx-ink);
  vertical-align: top;
}
.fx-page .fx-cut__table tbody tr:hover { background: #FAFAFB; }

/* The unit, once, beneath its symbol — not beside it, where it would double the
   column width, and not in every cell, where it would treble the row height. */
.fx-page .fx-cut__unit {
  display: block;
  margin-top: .2rem;
  font-size: .625rem;
  letter-spacing: .06em;
  text-transform: none;
  color: var(--fx-ink-3);
  font-weight: 400;
}
.fx-page .fx-cut__num { text-align: right; padding-right: 0 !important; }
.fx-page .fx-cut__table td.fx-cut__num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.fx-page .fx-cut__table th.fx-cut__num { text-align: right; }
.fx-page .fx-cut__iso {
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--fx-accent);
}
.fx-page .fx-cut__mat { min-width: 13rem; line-height: 1.45; }

/* The one thing on this page that is ours and not the manufacturer's: a label
   for two parameter sets the data sheet supplies under one description. */
.fx-page .fx-cut__set {
  display: block;
  margin-top: .3rem;
  font-family: var(--font-mono);
  font-size: .625rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--fx-ink-3);
}
.fx-page .fx-cut__gap { color: var(--fx-ink-3); font-style: italic; }
.fx-page .fx-cut__setnote,
.fx-page .fx-cut__foot {
  margin-top: 1rem;
  font-size: .8125rem;
  line-height: 1.6;
  color: var(--fx-ink-3);
  max-width: 78ch;
}
.fx-page .fx-cut__foot { margin-top: clamp(1.75rem, 3vw, 2.5rem); }
.fx-page .fx-cut__foot--gap { margin-top: .85rem; }
.fx-page .fx-cut__foot strong { color: var(--fx-ink); }

.fx-page .fx-cut__hint { display: none; }

@media (max-width: 767px) {
  /* Narrower gutters inside the scroller buy back two columns of legible width
     before the reader has to scroll for them. */
  .fx-page .fx-cut__table { min-width: 42rem; font-size: .75rem; }
  .fx-page .fx-cut__table th,
  .fx-page .fx-cut__table td { padding-right: .55rem; }

  /* THE ROW LABEL STAYS WHEN THE NUMBERS MOVE.
     Nine columns do not fit a phone, so the table scrolls — and a plain scrolling
     table hands the reader a column of speeds with the material it belongs to
     already off the left edge. Which is worse than not showing the numbers at all:
     a parameter read against the wrong material is a broken tool. The ISO group and
     the material pin to the left, opaque, with a rule down their right so it is
     obvious the rest has moved underneath them. */
  /* 2.6rem is the width of the ISO column, and the second sticky column starts
     exactly there. They have to agree: set the offset any narrower and the
     material header slides under the ISO header, which on a 390px screen reads as
     the single word ISOWORKPIECE. */
  .fx-page .fx-cut__table th:first-child,
  .fx-page .fx-cut__table td.fx-cut__iso {
    position: sticky; left: 0; z-index: 2;
    background: #fff;
    width: 2.6rem; min-width: 2.6rem;
    padding-left: .25rem; padding-right: .35rem;
  }
  .fx-page .fx-cut__table th:nth-child(2),
  .fx-page .fx-cut__table td.fx-cut__mat {
    position: sticky; left: 2.6rem; z-index: 2;
    background: #fff; min-width: 8.5rem;
    box-shadow: 1px 0 0 var(--fx-rule);
  }
  .fx-page .fx-cut__table tbody tr:hover td.fx-cut__iso,
  .fx-page .fx-cut__table tbody tr:hover td.fx-cut__mat { background: #FAFAFB; }

  /* Said once, above the first table. Without it the only clue that eight of the
     nine columns exist is a scrollbar the phone hides until you touch it. */
  .fx-page .fx-cut__hint {
    display: block;
    margin: .1rem 0 .9rem;
    font-family: var(--font-mono);
    font-size: .6875rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--fx-ink-3);
  }
}

/* ------------------------------------------------- products: tool families --
   THE TOOL STANDS UP, AND THAT IS THE WHOLE IDEA.
   What this replaces: one published family as a wide horizontal editorial block
   with two typographic blocks beneath it. That arrangement ranked the families
   correctly and still read as "a big picture and some text" rather than as a
   tool programme — the thing a machinist recognises at a glance is the tool,
   standing, at a size you can read the geometry from.

   Three equal positions now, each one an object over its own name. Equal
   positions are the point: the drill families hold the same footprint the Z5
   tool occupies, so the page reads as a programme with two positions not yet
   filled, and an approved vertical drill photograph drops into the same slot
   later without any of this being touched.

   THE GROUND IS ALREADY GONE, and it was not removed here. The upright frame is
   z5-end-mill-vertical, master C, which was photographed on white: every corner
   and every edge midpoint measures #FFFFFF, and the tool occupies 63% of the
   frame's width and 92% of its height. On a white canvas there is nothing to
   dissolve, no cast to correct and no mask to tune — which is why this component
   carries no filter at all, unlike the two crops of master J elsewhere on the
   site. Choosing the right existing frame did the work that CSS had to do there. */

.fx-page .fx-lineup {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.75rem, 3.5vw, 4rem);
}
.fx-page .fx-lineup__item {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: .7rem;
  text-decoration: none; color: inherit;
  border-top: 1px solid var(--fx-rule);
  padding: 1.1rem .25rem 0;
  transition: border-top-color 160ms ease;
}
.fx-page .fx-lineup__item:hover { border-top-color: var(--fx-ink); }
.fx-page .fx-lineup__item:focus-visible { outline: 2px solid var(--fx-accent); outline-offset: 6px; }

/* ONE STAGE HEIGHT FOR ALL THREE, and it is a height rather than a ratio because
   the subject is a standing object: what has to agree across the row is how tall
   the tools are, not how wide their frames happen to be. 400px at 1440 puts the
   Z5 tool at 368 of its own pixels — it fills 92% of the frame — which is inside
   the 300-420 the programme was specified at. */
.fx-page .fx-lineup__stage {
  position: relative;
  align-self: stretch;
  height: clamp(288px, 27vw, 400px);
  display: flex; align-items: flex-end; justify-content: center;
  margin-bottom: .35rem;
}
.fx-page .fx-lineup__stage picture { display: block; height: 100%; }
.fx-page .fx-lineup__img {
  display: block; height: 100%; width: auto; max-width: 100%;
  object-fit: contain;
  /* THE GROUND CAME BACK WITH THE PHOTOGRAPH, so the treatment comes back with it.
     The upright frame was master C until 2026-09-18, photographed on white, which is
     why this rule carried nothing. It is a derivative of master J now, and master J
     was shot on a warm studio sweep measuring R-B +13 — on a white page that reads
     as the pink rectangle this programme exists to not have.
     Same three terms as the other two crops of this master, and the same reasoning:
     saturate takes the cast out, contrast(1.21) takes the ground to white so it
     stops being a shape, and it is held there because past it the tone curve starts
     eating the shank's highlight rather than the background. */
  filter: saturate(.5) brightness(1.03) contrast(1.21);
  /* NO FADE AT THE BOTTOM, and that is the whole difference from the horizontal
     crops. The tool is standing: its shank runs off the lower edge of the frame, so
     a fade there would dissolve the base the tool appears to stand on. The other
     three sides carry nothing but sweep — the cutting head stops 13% down and the
     body spans 19%-80% across — so they can dissolve freely. */
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 9%, #000 100%);
  mask-image:
    linear-gradient(90deg, transparent 0, #000 10%, #000 90%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 9%, #000 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
/* THE RESERVED POSITIONS ARE AN AXIS, NOT A TOOL.
   A silhouette here would be a drawing of a product that does not exist yet, and
   this site does not draw those. A single hairline on the centre line is the line
   the tool will stand on: it reads as a position being held rather than as an
   image that failed to load, and it invents nothing. Drawn with a pseudo-element
   so the markup carries no img, picture, svg or background-image — which is also
   what tests/productImagery.test.js requires of a family that publishes nothing. */
.fx-page .fx-lineup__item--reserved .fx-lineup__stage::before {
  content: ""; position: absolute; left: 50%; bottom: 0;
  width: 1px; height: 92%;
  background: linear-gradient(180deg, transparent 0, var(--fx-rule) 18%, var(--fx-rule) 100%);
}
.fx-page .fx-lineup__item--reserved .fx-lineup__stage::after {
  content: ""; position: absolute; left: 50%; bottom: 0;
  width: 34px; height: 1px; transform: translateX(-50%);
  background: var(--fx-rule);
}

.fx-page .fx-lineup__flag {
  font-family: var(--font-mono); font-size: .6875rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--fx-accent);
}
.fx-page .fx-lineup__flag--pending { color: var(--fx-ink-3); }
.fx-page .fx-lineup__name {
  font-size: clamp(1.15rem, 1.5vw, 1.4rem); line-height: 1.22;
  letter-spacing: -.018em; margin: 0; font-weight: 700;
}
.fx-page .fx-lineup__name em { display: block; font-style: normal; color: var(--fx-ink-2); font-weight: 500; }
.fx-page .fx-lineup__note { font-size: .875rem; line-height: 1.6; color: var(--fx-ink-2); margin: 0; max-width: 32ch; }
.fx-page .fx-lineup__facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem .7rem; margin: .2rem 0 0; width: 100%; max-width: 22rem; }
.fx-page .fx-lineup__facts > div { border-top: 1px solid var(--fx-rule); padding-top: .45rem; }
.fx-page .fx-lineup__facts dt { font-size: .6875rem; color: var(--fx-ink-3); }
.fx-page .fx-lineup__facts dd {
  margin: .1rem 0 0; font-family: var(--font-mono); font-size: .8125rem; color: var(--fx-ink);
}
.fx-page .fx-lineup__cta {
  margin-top: auto; padding-top: .8rem;
  font-size: .875rem; font-weight: 600; color: var(--fx-accent);
  transition: color 160ms ease;
}
.fx-page .fx-lineup__item:hover .fx-lineup__cta { color: var(--fx-ink); }

@media (max-width: 899px) {
  /* Stacked, and the tool STAYS UPRIGHT. The stage is a height, so a narrower
     column shortens nothing — the tool is the same object, read the same way,
     with the row's shared footprint preserved down the page. */
  .fx-page .fx-lineup { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; }
  .fx-page .fx-lineup__stage { height: 320px; }
  .fx-page .fx-lineup__note { max-width: 46ch; }
}

/* ---------------------------------------------------------------- long tokens

   A FILE PATH IS NOT A WORD, AND THE PAGE SHOULD NOT SCROLL SIDEWAYS FOR ONE.

   Measured 2026-09-18 on /products/drills-3d/ and /products/drills-5d/ at a 390px
   device width: documentElement.clientWidth 390, scrollWidth 416. The whole of the
   difference was one inline <code> holding

       docs/manufacturer/DRILLS_TECHNICAL_DATA_REQUEST.csv

   which renders 393px wide as a single unbreakable token — no space, and default
   line-breaking does not break at "/" or "_". It sat at left 20, right 413, so it
   pushed 23px past the viewport and the page scrolled horizontally on a phone.

   Every other page measured 390/390, and the two controls either side of these
   (/products/ and /products/end-mills-z5/) still do. This is the smallest correction
   that removes it: the token wraps instead of overflowing. Nothing about the
   typography, the spacing, the content or the release-in-preparation messaging
   changes, and at every width where the token already fits nothing moves at all.

   `break-word` rather than `anywhere` on purpose — it breaks a token only when that
   token cannot fit on a line of its own, and it leaves intrinsic min-content sizing
   alone, so no grid or flex track is re-measured as a side effect. base.css already
   applies the same property to h1/h2/h3 for the same reason.

   Scoped under .fx-page, like every other rule in this sheet, so it cannot reach the
   approved homepage — which does not load this stylesheet and carries no <code>. */
.fx-page code { overflow-wrap: break-word; }
