@charset "utf-8";

/* EDI Electronics Outlet
   The whole stylesheet. Sections 3 and 4 of BUILD-SPEC.md, in spec order.

   Reading order below: font faces, tokens, reset, base elements, utilities, focus,
   grid, motion, then every component in section 4.

   Three things a reader will otherwise trip over, all of them load-bearing:

   1. Colour. Nothing here uses a colour that is not already a token in 3.7, because a new
      colour without a measured luminance and a measured ratio is banned by rule 42. The one
      literal is #FFFFFF, and it is literal on purpose: on the green fill, the heading token
      measures 4.13 and fails, so the white is written out at every point where text can land
      on green rather than left to inherit.
   2. Motion. Only the 6 rows of the 3.13 table animate. Every transition below names its
      properties one at a time, so nothing new can ride in on a shorthand.
   3. Radius. Two values, 0 and 2px, plus 50% on the 2 counter-state dots and nothing else.

   Naming. Every selector here is written against the class the page generator actually emits.
   Where this file once carried a second private name for something the generator already
   named, the rule was renamed rather than copied, because 2 names for 1 thing is how the
   header, the certification row and the buy column came to render bare in the first place.
*/

/* ---------------------------------------------------------------------------
   FONT FACES, 3.8
   --------------------------------------------------------------------------- */

/* Archivo variable, weight axis only, latin subset, self-hosted. The width axis is not
   loaded: it costs 90,104 bytes for a variation this design uses twice. swap rather than
   optional because the fallback below is metric-matched, so the swap does not move text. */
@font-face{
  font-family:"Archivo";
  src:url("archivo.woff2") format("woff2");
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD;
}

/* Metric override so nothing jumps when the real font lands. Archivo x-height is 0.5260 em
   against Arial at 0.5186, which is where 101.4% comes from. */
@font-face{
  font-family:"Archivo Fallback"; src:local("Arial");
  size-adjust:101.4%; ascent-override:92%; descent-override:24%; line-gap-override:0%;
}

/* ---------------------------------------------------------------------------
   TOKENS, 3.7 and 3.8
   --------------------------------------------------------------------------- */

:root{
  /* surfaces */
  --surface-page:#17191A; --surface-default:#26292A;
  --surface-raised:#2E3132; --surface-pressed:#383B3C;
  --plinth:#EDEEEA; --plinth-hover:#F3F4F1; --plinth-small:#E4E6E1;
  /* True white, for the certification cells only. Three of the six marks carry a white
     rectangle baked into the artwork, so on #EDEEEA you see that rectangle as a paler patch.
     On #FFFFFF it disappears and the marks read as one set. */
  --plinth-white:#FFFFFF;
  --plinth-ink:#2C2D2D;

  /* text */
  --text-heading:#F2F3F1; --text-body:#F2F3F1; --text-muted:#AEB6B2;

  /* green, split into 2 roles. The split is not optional. */
  --accent-fill:#0C8566;     /* fills only, #FFFFFF hardcoded on it */
  --accent-on-dark:#12B88C;  /* text and information only */
  /* eBay's four brand colours, on the proof link in the reputation band and nowhere else.
     They do not move between the themes, because they are somebody else's brand and not
     ours. The eBay wordmark itself is NOT reproduced anywhere on this site: a logo drawn
     from memory is a worse fault than no logo. */
  --ebay-red:#E53238; --ebay-blue:#0064D2; --ebay-yellow:#F5AF02; --ebay-green:#86B817;
  --focus-ring:#4CC9A3;
  --accent-rgb:12,133,102;

  /* state */
  --state-closed:#E8B14C;    /* 9.09 / 7.55 / 6.76. Counter rail only */
  --stamp:#E8C46A;           /* review builds only, never ships live */
  --stamp-ink:#17191A;

  /* structure */
  --hairline:rgb(255 255 255 / .08);
  --hairline-strong:rgb(255 255 255 / .14);
  --edge:#808282;

  /* radii */
  --r-0:0;    /* panels, tiles, photographs, plinths, tables, sheets */
  --r-1:2px;  /* buttons, inputs, chips, tags */

  /* layout */
  --wrap:1440px;
  --gutter:clamp(1rem,3.2vw,3rem);
  --header-h:108px; /* overwritten at runtime by ResizeObserver */
}

:root{
  --ff-sans:"Archivo","Archivo Fallback","Segoe UI",system-ui,Arial,sans-serif;
  --ff-mono:ui-monospace,"Cascadia Mono","SF Mono",Menlo,Consolas,monospace;

  /* The floor is 2.125rem, not the 2.75rem the specification names. At 320px, 2.75rem sets
     "WORKSTATIONS," at about 370px inside 288px of content, so the page scrolled sideways on a
     phone, which acceptance item 18 forbids outright. Nothing above 531px changes, and the cap
     is untouched. A headline that overflows is worse than a headline that is 10px smaller. */
  --t-display:clamp(2.125rem,6.4vw,4.5rem);  --lh-display:0.95; --ls-display:-0.022em;
  --t-h1:clamp(2.25rem,4.4vw,3.25rem);      --lh-h1:1.00;      --ls-h1:-0.018em;
  --t-h2:clamp(1.75rem,3.0vw,2.25rem);      --lh-h2:1.06;      --ls-h2:-0.012em;
  --t-h3:1.5rem;      --lh-h3:1.15;   --ls-h3:-0.006em;
  --t-h4:1.25rem;     --lh-h4:1.25;   --ls-h4:0;
  --t-lede:1.125rem;  --lh-lede:1.55; --ls-lede:0;
  --t-body:1rem;      --lh-body:1.60; --ls-body:0;
  --t-small:0.875rem; --lh-small:1.50;--ls-small:0;
  --t-caption:0.8125rem; --lh-caption:1.45; --ls-caption:0;
  --t-eyebrow:0.6875rem; --lh-eyebrow:1.20; --ls-eyebrow:0.14em;
  --t-price:2rem;        --lh-price:1.00;   --ls-price:-0.012em;
  --t-price-2:1.125rem;  --lh-price-2:1.20;
  --t-cell-price:1.25rem;--lh-cell-price:1.00; --ls-cell-price:-0.012em;
  --t-cell-name:0.875rem;--lh-cell-name:1.35;
  --t-list-name:0.9375rem; --lh-list-name:1.30;
  --t-ref:0.8125rem;     --lh-ref:1.20;     --ls-ref:0.04em;
}

/* ---------------------------------------------------------------------------
   RESET
   --------------------------------------------------------------------------- */

*,*::before,*::after{ box-sizing:border-box; }

/* The search field filters by toggling hidden on cells that are display:flex, so this has to
   win outright or a filtered-out product stays on screen. */
[hidden]{ display:none !important; }

html{
  color-scheme:dark;
  -webkit-text-size-adjust:100%;
  text-size-adjust:100%;
}

body{
  margin:0;
  background:var(--surface-page);
  color:var(--text-body);
  font-family:var(--ff-sans);
  font-size:var(--t-body);
  line-height:var(--lh-body);
  font-weight:400;
  font-synthesis-weight:none;
}

h1,h2,h3,h4,h5,h6,p,figure,blockquote,dl,dd{ margin:0; }
ul,ol{ margin:0; padding:0; }
li{ list-style:none; }

img,picture,svg,video{ display:block; max-width:100%; }
img{ height:auto; }

button,input,select,textarea{
  font:inherit; color:inherit; letter-spacing:inherit; margin:0;
}
button{ background:none; border:0; padding:0; cursor:pointer; }

fieldset{ margin:0; padding:0; border:0; min-inline-size:0; }
legend{ padding:0; }

table{ border-collapse:collapse; inline-size:100%; }

/* Archivo ships no slanted file and nothing on this site is ever slanted. Without this the
   browser synthesises a slant for these elements, which is the exact thing 3.8 rules out.
   Emphasis is carried by weight instead. */
em,i,cite,dfn,var,address{ font-style:normal; }
em,i{ font-weight:600; }

/* ---------------------------------------------------------------------------
   BASE ELEMENTS
   --------------------------------------------------------------------------- */

h1{ font-size:var(--t-h1); line-height:var(--lh-h1); letter-spacing:var(--ls-h1);
    font-weight:700; color:var(--text-heading); text-wrap:balance; }
h2{ font-size:var(--t-h2); line-height:var(--lh-h2); letter-spacing:var(--ls-h2);
    font-weight:700; color:var(--text-heading); text-wrap:balance; }
h3{ font-size:var(--t-h3); line-height:var(--lh-h3); letter-spacing:var(--ls-h3);
    font-weight:600; color:var(--text-heading); }
h4{ font-size:var(--t-h4); line-height:var(--lh-h4); letter-spacing:var(--ls-h4);
    font-weight:600; color:var(--text-heading); }

strong,b{ font-weight:700; color:var(--text-heading); }
small{ font-size:var(--t-small); line-height:var(--lh-small); }

/* A stray link still has to read as a link, because the pages are written by a script and an
   unstyled anchor on a dark ground is invisible. Components that are not prose links turn the
   underline off again below.

   :where() so this carries no specificity at all. Without it, a:hover would beat .btn-primary
   and paint the mint over the green fill at 1.45, which is exactly the silent failure rule 38
   is written about. Every component rule below now wins on its own. */
:where(a){
  color:var(--text-heading);
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:.18em;
}
:where(a:hover){ color:var(--accent-on-dark); }

::placeholder{ color:var(--text-muted); opacity:1; }
::selection{ background:var(--surface-pressed); color:var(--text-heading); }

/* ---------------------------------------------------------------------------
   UTILITIES
   --------------------------------------------------------------------------- */

.vh{
  position:absolute; inline-size:1px; block-size:1px;
  padding:0; margin:-1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap; border:0;
}

/* One of the 3 permitted monospace uses: item references, manufacturer model codes, and the
   4 ordinals on /testing/. Never on prose and never on a price. */
.mono{
  font-family:var(--ff-mono);
  font-size:var(--t-ref); line-height:var(--lh-ref); letter-spacing:var(--ls-ref);
}

.num{ font-variant-numeric:tabular-nums; }

/* A phone number is one thing to read, so it never breaks across 2 lines. On a call button
   the line breaks after the word Call or not at all. */
.callnum{ white-space:nowrap; }

.prose p,.prose li{ font-size:var(--t-body); line-height:var(--lh-body); max-inline-size:68ch; }
.prose p + p{ margin-block-start:1rem; }
.prose h2{ margin-block:3rem 1rem; }
.prose h3{ margin-block:2rem .75rem; }

/* 3.12. On a dark ground a drop shadow does almost nothing, the 1px top highlight is what
   sells elevation. Exactly 3 things carry this: the sticky header with its rail, the mobile
   drawer and the mobile action bar. The drawer and the bar carry the class in the markup; the
   header is named here so a 4th thing cannot quietly acquire the treatment. */
.floating,
.hdr{
  background:var(--surface-default);
  box-shadow:inset 0 1px 0 rgb(255 255 255 / .08), 0 24px 48px -12px rgb(0 0 0 / .70);
}

/* ---------------------------------------------------------------------------
   FOCUS, and SC 2.4.11
   --------------------------------------------------------------------------- */

:where(a,button,input,select,summary,[tabindex]):focus-visible{
  outline:3px solid var(--focus-ring); outline-offset:2px; border-radius:inherit;
}

/* outline-offset puts the ring on the dark ground rather than on the green fill, where the
   mint measures 2.23 and disappears. The shadow fills that 2px gap with the page colour so
   the ring never touches the green. */
.btn-primary:focus-visible{ box-shadow:0 0 0 2px var(--surface-page); }

/* The mint ring against the light plinth measures 1.81 and is not usable, so anything
   focusable that lands on the certification band takes the plinth ink instead, at 11.85. */
.certs :focus-visible{ outline-color:var(--plinth-ink); }

/* The browser's own scroll-into-view for a newly focused element honours scroll-padding, which
   is what makes keyboard focus pass 2.4.11 and not just anchor jumps. --header-h is measured
   at runtime because the header changes height at 320px and at 200% zoom, which is exactly
   when this value matters. */
html{ scroll-padding-top: calc(var(--header-h) + 1rem); }
:target,[id],#main,h1,h2,h3{ scroll-margin-top: calc(var(--header-h) + 1rem); }
@media (max-height:450px){
  .hdr{ position:static; }
  html{ scroll-padding-top:1rem; }
}

/* ---------------------------------------------------------------------------
   GRID, 3.10
   --------------------------------------------------------------------------- */

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

/* THE SHEET. Borders on children right and bottom, container top and left.

   minmax(0,1fr) and not a bare 1fr, for the same reason 4.2 puts minmax(0,auto) on the
   counter rail. A bare 1fr floors every track at the cell's own min-content, and a cell whose
   footer holds a 15 character reference on one unbreakable line measures 422px, so 4 tracks
   came to 1,692px inside a 1,425px page and the home page scrolled sideways at 1440 by 253px.
   With the floor removed the tracks are 356px at 1440, which is the cell size 3.10 states in
   its own arithmetic, and nothing inside a cell overflows it. */
.sheet{
  display:grid; gap:0; grid-template-columns:repeat(2,minmax(0,1fr));
  border-top:1px solid var(--hairline);
  border-left:1px solid var(--hairline);
}
.sheet > *{
  border-right:1px solid var(--hairline);
  border-bottom:1px solid var(--hairline);
}
@media (min-width:700px){  .sheet{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
@media (min-width:1040px){ .sheet{ grid-template-columns:repeat(4,minmax(0,1fr)); } }

.sheet--gallery{ grid-template-columns:minmax(0,1fr); }
@media (min-width:700px){ .sheet--gallery{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
.sheet--list{ grid-template-columns:minmax(0,1fr); }

.cell--wide{ grid-column:span 2; }
.cell--full{ grid-column:1 / -1; }

/* ITEM AND MODEL PAGES */
.item{
  display:grid; grid-template-columns:repeat(12,minmax(0,1fr));
  column-gap:clamp(1.5rem,3vw,3rem); row-gap:2rem;
}
.item__media{ grid-column:1 / span 7; }
.item__buy{
  grid-column:8 / span 5;
  position:sticky; top:calc(var(--header-h) + 1.5rem); align-self:start;
}
@media (max-width:900px){
  .item__media,.item__buy{ grid-column:1 / -1; }
  .item__buy{ position:static; }
}

/* SHOP: fixed rail plus sheet */
@media (min-width:1040px){
  .shop{ display:grid; grid-template-columns:240px minmax(0,1fr); column-gap:0; }
  .shop__rail{ position:sticky; top:calc(var(--header-h) + 1.5rem); align-self:start;
               border-right:1px solid var(--hairline); padding-right:1.5rem; }
  .shop__sheet{ padding-left:1.5rem; }
}

/* At 1440 with a 48px gutter, 4 columns gives 336px cells, a plinth of 336 by 252 and a cell
   about 390px tall. That is where contain-intrinsic-size comes from, further down. */

/* ---------------------------------------------------------------------------
   THE 8 PAGE CLASSES, on <body>
   --------------------------------------------------------------------------- */

/* These carry the 3 differences the specification actually asks for and nothing else.

   1. Two page types, home and visit, open with a full bleed band that has to meet the counter
      rail with no gap, so only those two carry no separation above <main>. Every other type
      opens with a breadcrumb inside .wrap and needs the air.
   2. 3.9 permits 2 section values and no others, 4rem and 6rem. That is what closes every
      page above the certification band, which is a light band and needs the dark page to
      finish before it starts.
   3. The item page is the only one carrying the fixed mobile bar, so it is the only one that
      has to reserve room under the last thing on the page. */
.page-home main,.page-visit main{ padding-block:0 4rem; }
.page-404 main,.page-shop main,.page-item main,
.page-model main,.page-business main,.page-prose main{ padding-block:1rem 4rem; }
@media (min-width:900px){
  .page-home main,.page-visit main{ padding-block:0 6rem; }
  .page-404 main,.page-shop main,.page-item main,
  .page-model main,.page-business main,.page-prose main{ padding-block:1rem 6rem; }
}

/* ---------------------------------------------------------------------------
   MOTION, 3.13. Nothing outside this inventory animates.
   --------------------------------------------------------------------------- */

/* .01ms and never none. none stops transitionend firing, and the drawer's close handler
   waits on that event, so none leaves the drawer stuck open. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important;
  }
}
html{ scroll-behavior:smooth; }
@media (prefers-reduced-motion:reduce){ html{ scroll-behavior:auto; } }

/* ---------------------------------------------------------------------------
   REVIEW BUILD ONLY
   --------------------------------------------------------------------------- */

/* Neither of these reaches a live deploy set: the publish guard fails the build on the
   unconfirmed-fact marker. This comment used to name that marker in full, which made the
   guard fail on its own documentation and blocked every cloud build.
   They exist so a review build shows the tag where the fact would sit, and
   shows at a glance that it is a review build. --stamp measures 10.53 on the page ground and
   its ink measures 10.53 the other way, on the tag. */
.reviewbar{
  padding:.5rem var(--gutter);
  background:var(--stamp); color:var(--stamp-ink);
  font-size:var(--t-small); line-height:var(--lh-small); font-weight:600;
}
.stamp{
  display:inline-block;
  padding:.125rem .375rem;
  background:var(--stamp); color:var(--stamp-ink);
  border-radius:var(--r-1);
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; font-weight:700;
}

/* The blocks that hold nothing but unanswered questions. They are set as quietly as the rest
   of the page: rule 43 forbids marking an unknown with red or amber, and the yellow above is
   on the tag itself, which is a build marker and not a warning about the fact. */
.confirm{
  margin-block-start:3rem; padding-block-start:1rem;
  border-block-start:1px solid var(--hairline-strong);
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}
.confirm h2{
  font-size:var(--t-h4); line-height:var(--lh-h4); letter-spacing:var(--ls-h4);
  font-weight:700; margin-block:0 .75rem;
}
.confirm p + p{ margin-block-start:.75rem; }

/* ---------------------------------------------------------------------------
   4.1 HEADER
   --------------------------------------------------------------------------- */

.hdr{
  position:sticky; inset-block-start:0; z-index:100;
}

/* Row A, 64px. The logo and the nav are hard left, in that order, and the phone is the only
   thing on the right. No cart, no account, no search glyph. */
.hdr__row{
  display:flex; align-items:center; gap:1.5rem;
  min-block-size:64px; padding-inline:var(--gutter);
}

.skip{
  position:absolute; inline-size:1px; block-size:1px;
  padding:0; margin:-1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}
.skip:focus{
  position:fixed; inset-block-start:0; inset-inline-start:0; z-index:300;
  inline-size:auto; block-size:auto; margin:0; padding:.75rem 1rem;
  clip-path:none; overflow:visible;
  background:var(--accent-fill);
  border-radius:var(--r-1);
  font-size:var(--t-small); font-weight:700; text-decoration:none;
}

.hdr__logo{ display:flex; align-items:center; flex:0 0 auto; text-decoration:none; }
.hdr__logo img{ inline-size:182px; block-size:35px; }

.hdr__nav ul{ display:flex; align-items:center; gap:.25rem; flex-wrap:wrap; }
.hdr__nav a{
  display:inline-flex; align-items:center;
  min-block-size:44px; padding-inline:.625rem;
  border-block-end:2px solid transparent;
  color:var(--text-body); text-decoration:none;
  font-size:var(--t-small); line-height:var(--lh-small); font-weight:500;
  transition:background-color 120ms linear, border-color 120ms linear;
}
.hdr__nav a:hover{ background:var(--surface-raised); color:var(--text-body); }
/* State is never colour alone: the active link carries a rule and a weight as well. */
.hdr__nav a[aria-current="page"]{
  color:var(--text-heading); font-weight:700;
  border-block-end-color:var(--accent-on-dark);
}

.hdr__tel{
  margin-inline-start:auto;
  display:inline-flex; align-items:center;
  min-block-size:44px; padding-inline:.75rem; margin-inline-end:-.75rem;
  border-radius:var(--r-1);
  color:var(--text-heading); text-decoration:none;
  font-size:var(--t-small); font-weight:600; font-variant-numeric:tabular-nums;
  white-space:nowrap;
  transition:background-color 120ms linear;
}
.hdr__tel:hover{ background:var(--surface-raised); color:var(--text-heading); }

.hdr__burger{
  display:none;
  align-items:center; gap:.5rem;
  min-block-size:44px; min-inline-size:44px; padding-inline:.75rem;
  border:1px solid var(--edge); border-radius:var(--r-1);
  background:var(--surface-default); color:var(--text-heading);
  font-size:var(--t-small); font-weight:600;
  transition:background-color 120ms linear, border-color 120ms linear;
}
.hdr__burger:hover{ background:var(--surface-raised); }

/* 3 rules drawn in the control's own text colour, never a drawn line icon from a set, and
   never on its own: the word Menu sits beside it and is the accessible name. */
.hdr__glyph{
  flex:0 0 auto;
  inline-size:16px; block-size:2px;
  background:currentColor;
  box-shadow:0 -5px 0 currentColor, 0 5px 0 currentColor;
}

/* The logo is 182px, the phone is 123px and the burger with its word is 72px. Those 3 do not
   fit one 288px line at 320px and they never will, so the row is allowed to take a second
   line rather than squeezing the word out of the burger or pushing the phone off the page.
   The header measures its own height at runtime, so the sticky offsets follow it.

   1023 AND NOT 900 SINCE 2026-09-09, when Contact us made five nav items. Measured at 940:
   the list needs 496px and the row gives it 478, so the fifth item wrapped onto a line of its
   own under the logo. Five items, the logo and a phone number with an extension on it need
   about 1020px of header before something has to give, so the compact header starts there.
   This block is the header only. The fixed call bar at the foot stays at 900. */
@media (max-width:1023px){
  .hdr__nav{ display:none; }
  .hdr__row{ flex-wrap:wrap; gap:.5rem 1rem; padding-block:.5rem; }
  .hdr__burger{ display:inline-flex; flex:0 0 auto; margin-inline-start:auto;
                white-space:nowrap; }
  .hdr__tel{ margin-inline-start:0; }
}

/* ---------------------------------------------------------------------------
   4.2 COUNTER RAIL
   --------------------------------------------------------------------------- */

.rail{
  display:grid; grid-template-columns:minmax(0,auto) minmax(0,1fr);
  align-items:center; column-gap:1.5rem; min-height:44px;
  padding-inline:var(--gutter);
  background:var(--surface-default);
  border-block:1px solid var(--hairline);
  font-size:var(--t-caption); line-height:var(--lh-caption);
}
/* minmax(0,auto) on the first track and min-width:0 here, so a long hours line is ellipsed
   inside its own cell instead of pushing the address out of the gutter. The original auto 1fr
   auto did that between roughly 701px and 850px, which includes portrait tablets and a laptop
   at 200% zoom. The trailing auto track went with the rail's phone number: the header carries
   the number, one corner, once. */
.rail__state,
.rail__where{ margin:0; display:flex; align-items:baseline; gap:.5rem; min-width:0; }
.rail__dot{ flex:0 0 auto; align-self:center; width:8px; height:8px; border-radius:50%;
            background:var(--text-muted); }
.rail[data-state="open"]   .rail__dot{ background:var(--accent-on-dark); }
.rail[data-state="closed"] .rail__dot{ background:var(--state-closed); }
.rail__word{ font-weight:700; text-transform:uppercase; letter-spacing:var(--ls-eyebrow);
             color:var(--text-muted); white-space:nowrap; }
.rail[data-state="open"]   .rail__word{ color:var(--accent-on-dark); }
.rail[data-state="closed"] .rail__word{ color:var(--state-closed); }
.rail__detail,.rail__addr{ color:var(--text-muted); font-variant-numeric:tabular-nums;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
.rail__where{ justify-self:start; }
.rail__dir{ color:var(--text-heading); text-decoration:underline;
            text-underline-offset:.18em; white-space:nowrap; flex:0 0 auto; }
.rail__dir:hover{ color:var(--accent-on-dark); }
/* The hours used to be dropped below 900px because the rail's own phone number needed the
   room. The number has gone to the header, so the hours can be shown instead, and they are
   the only thing on this strip that says anything: a strip reading COUNTER HOURS with no
   hours after it is a label with nothing in it. Wrapping, never ellipsed, because a closing
   time cut to "4:3" is worse than no closing time at all. */
@media (max-width:900px){
  .rail__state{ flex-wrap:wrap; }
  .rail__detail{ white-space:normal; overflow:visible; text-overflow:clip; }
}
@media (max-width:700px){
  .rail{ grid-template-columns:minmax(0,1fr); min-height:44px; column-gap:1rem; }
  .rail__where{ display:none; }
}
@media (max-width:360px){ .rail__word{ letter-spacing:0; } }

/* ---------------------------------------------------------------------------
   4.3 MOBILE DRAWER
   --------------------------------------------------------------------------- */

/* The handshake with the script: the drawer carries hidden while shut. The script removes
   hidden with data-state="closed" still set, forces a reflow, then sets data-state="open",
   which is what gives the panel something to transition from. On close it sets
   data-state="closed" and waits for transitionend before putting hidden back, which is why
   reduced motion uses .01ms and never none.

   The generator ships the panel itself as .drawer with the scrim as its next sibling, not as
   a child, so the sliding element and the scrim are addressed in that shape. site.js watches
   transitionend on .drawer__panel where one exists and on the drawer itself where one does
   not, so this arrangement is the one it already expects. */
.drawer{
  position:fixed; inset-block:0; inset-inline-end:0; z-index:200;
  inline-size:min(88vw,22rem);
  padding:1rem var(--gutter) 2rem;
  overflow-y:auto; overscroll-behavior:contain;
  transform:translateX(0);
  transition:transform 180ms cubic-bezier(.2,0,0,1);
}
.drawer[data-state="closed"]{ transform:translateX(100%); }

.drawer__scrim{
  position:fixed; inset:0; z-index:190;
  background:var(--surface-page);
  opacity:.86;
  transition:opacity 180ms linear;
}
.drawer[data-state="closed"] + .drawer__scrim{ opacity:0; }

.drawer__close{
  display:inline-flex; align-items:center;
  min-block-size:44px; padding-inline:.75rem;
  border:1px solid var(--edge); border-radius:var(--r-1);
  background:var(--surface-default); color:var(--text-heading);
  font-size:var(--t-small); font-weight:600;
  transition:background-color 120ms linear, border-color 120ms linear;
}
.drawer__close:hover{ background:var(--surface-raised); }

.drawer nav{ margin-block:1.5rem; }
.drawer nav a{
  display:flex; align-items:center;
  min-block-size:52px;
  border-block-end:1px solid var(--hairline);
  color:var(--text-heading); text-decoration:none;
  font-size:var(--t-lede); font-weight:600;
  transition:background-color 120ms linear;
}
.drawer nav a:hover{ background:var(--surface-raised); color:var(--text-heading); }
.drawer nav a[aria-current="page"]{ box-shadow:inset 3px 0 0 var(--accent-on-dark); }

/* 3.2.6, consistent help. The phone and the address sit at the foot of the drawer in the same
   order as they sit at the foot of every page. */
.drawer__tel{
  font-size:var(--t-lede); font-weight:700; font-variant-numeric:tabular-nums;
}
.drawer__addr{
  margin-block-start:.75rem;
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}

/* ---------------------------------------------------------------------------
   4.4 HERO
   --------------------------------------------------------------------------- */

.hero{
  /* minmax(0,1fr) and not 1fr. A bare 1fr track will not shrink below the widest unbreakable
     thing inside it, and the headline is one long uppercase word, so at 320px the column
     resolved to 408px and pushed the whole page sideways. Same floor the sheet hit. */
  display:grid; grid-template-columns:minmax(0,1fr); background:var(--surface-page);
  border-bottom:0;                     /* the sheet's own top rule provides the joint */
}
/* CENTRED, from 2026-09-09. Ruben chose this out of three: the stock figures as an eyebrow,
   the headline under them, one line under that, then the credentials straight across the full
   width. Symmetrical on purpose, so there is no wide column with nothing in it, which is what
   was wrong with the two-column version before it. */
.hero__text{
  padding:3.5rem var(--gutter) 3rem; text-align:center;
  max-inline-size:var(--wrap); margin-inline:auto;
}
.hero__claim{
  font-size:var(--t-lede); line-height:var(--lh-lede); color:var(--text-body);
  max-inline-size:34ch; margin:1.5rem auto 0;
}
.hero__h1{
  font-size:var(--t-display); line-height:var(--lh-display); letter-spacing:var(--ls-display);
  font-weight:500; text-transform:uppercase; color:var(--text-heading);
  max-inline-size:22ch; margin:1.25rem auto 0;
  text-wrap:balance;
}
.hero__h1 b{ font-weight:800; }
/* THE OPENING BLOCK'S SECOND COLUMN, built as cells of the same hairline grid the product
   sheet is made of, so the page is one system from the very top instead of a banner sitting
   above a grid. It held a photograph of the counter until 2026-09-09, when Ruben moved that
   to /visit/; the first attempt at replacing it floated the marks loose on the dark ground
   and he was right that it looked like clip art. NOTHING FLOATS NOW: every mark is a cell,
   every cell is the same size, and the lines are the sheet's own lines. */
.hero__proof{
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  border-inline-start:1px solid var(--hairline);
}
.hero__proof > *{
  border-block-start:1px solid var(--hairline);
  border-inline-end:1px solid var(--hairline);
}
/* The seal is the last cell of the rail. It spans the row at narrow widths, where the marks
   stack two and three across and a quarter-width seal would be unreadable. */
.hero__seal{ grid-column:1 / -1; }
.hero__badge{
  display:flex; align-items:center; justify-content:center;
}
/* THE SAME GREY AS THE PRODUCT TILES BELOW, not true white. Ruben, 2026-09-10, looking at the
   live site: "this strip of certifications is too white its mismatched from the white below".
   He is right, and the mismatch was deliberate, so here is what it cost and what it buys.

   FOUR of the six marks, not the three this note used to claim, carry solid white inside the
   artwork: CompTIA is 64% near-white opaque pixels, Controlled Goods 48%, ISO 14001 42%, ISO
   45001 30%. Measured, not guessed. On #EDEEEA each of those shows its own white as a slightly
   paler shape, which is what the true-white cell was hiding.

   Recolouring the artwork is not the way out. The white in the Controlled Goods mark is the
   field of the Canadian flag, and repainting a certifying body's emblem to suit our background
   is not ours to do. So the choice is one mismatch or the other, and a strip that reads as one
   surface with the shop is worth more than four marks that each hide their own edge. */
.hero__badge--plinth{ background:var(--plinth); }
/* No object-fit and no fixed height. The inset is baked into the asset, every asset is the
   same canvas, so the mark simply fills its cell and all six match without CSS arithmetic. */
.hero__badge img{ display:block; inline-size:100%; block-size:auto; }
@media (min-width:900px){
  .hero__text{ padding:5rem var(--gutter) 4rem; }
  .hero__proof{ grid-template-columns:repeat(3,minmax(0,1fr)); }
}

/* All seven cells in one row. Below this the seal gets less than about 15rem, its heading
   wraps to three lines, and the rail stops reading as a rail. */
@media (min-width:1120px){
  .hero__proof{ grid-template-columns:repeat(6,minmax(0,1fr)) minmax(17rem,20rem); }
  .hero__seal{ grid-column:auto; }
}

/* The stock readout. Every figure in it is written by the nightly script. */
.hero__stock{
  display:flex; flex-wrap:wrap; justify-content:center; gap:.5rem 1.5rem;
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow);
  letter-spacing:var(--ls-eyebrow); text-transform:uppercase;
  font-variant-numeric:tabular-nums;
}
.hero__pair{ display:flex; align-items:baseline; gap:.5rem; }
.hero__stock dt{ color:var(--text-muted); }
.hero__stock dd{ margin:0; color:var(--text-heading); font-weight:700; }

/* ---------------------------------------------------------------------------
   THE PLINTH, 3.1 and section 9
   --------------------------------------------------------------------------- */

/* The product photographs were shot over months by different people, on white or near-white,
   at widths from 333px to 500px. One fixed box with contain and 12% padding is what makes
   them read as one collection rather than as 197 pasted cutouts on charcoal.

   The plinth is #EDEEEA and not #FFFFFF because seller white is rarely pure, and near-white
   on pure white shows up as a faint grey rectangle around every product.

   isolation:isolate is mandatory and is not an optimisation. mix-blend-mode resolves against
   the nearest stacking context, and without this the blend falls through to the charcoal page
   and the photographs disappear. It must not depend on content-visibility, which is a
   performance property somebody will one day remove. */
.cell__plinth{
  --plinth-c:var(--plinth);
  position:relative; aspect-ratio:4 / 3;
  background:var(--plinth-c);
  display:grid; place-items:center; padding:12%;
  /* minmax(0,1fr) is load bearing. Without it the implicit row track sizes to the image's
     own height, the img's height:100% resolves against that grown track instead of the
     aspect-locked box, and overflow:hidden then cuts the bottom off the product. It clipped
     more than half the catalogue, some photographs through the middle of the item, which is
     the exact outcome the contain rule below exists to prevent. */
  grid-template-rows:minmax(0,1fr);
  overflow:hidden;
  border-radius:var(--r-0);
  box-shadow:inset 0 0 0 1px rgb(0 0 0 / .10);
  isolation:isolate;
  transition:background-color 120ms linear;
}

/* contain, never cover. A cover crop on a product photograph cuts the product. */
.cell__plinth img{
  width:100%; height:100%;
  object-fit:contain;
  mix-blend-mode:multiply;
}

/* ---------------------------------------------------------------------------
   4.5 PRODUCT CARD, THE SHEET CELL
   --------------------------------------------------------------------------- */

.cell{
  position:relative; display:flex; flex-direction:column; padding:1rem;
  background:var(--surface-page);
  transition:background-color 120ms linear;
  content-visibility:auto; contain-intrinsic-size:auto 390px;
  isolation:isolate;                 /* keeps the blend honest, see the plinth note above */
}
.cell:hover,.cell:focus-within{ background:var(--surface-raised); }
.cell:has(a:focus-visible){ background:var(--surface-raised); }
.cell:hover .cell__plinth{ --plinth-c:var(--plinth-hover); }

/* Exactly 1 link per cell. The hit area comes from this overlay and never from a wrapping
   anchor, which would swallow the whole cell into one unreadable link name. */
.cell__name{
  margin-block:.75rem 0;
  font-size:var(--t-cell-name); line-height:var(--lh-cell-name); font-weight:500;
  color:var(--text-body);
  display:-webkit-box; -webkit-box-orient:vertical;
  -webkit-line-clamp:2; line-clamp:2;
  overflow:hidden;
}
.cell__name a{ color:inherit; text-decoration:none; }
.cell__name a::after{ content:""; position:absolute; inset:0; }

.cell__cond{
  margin-block-start:.5rem;
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; color:var(--text-muted);
}

/* The footer row makes a real edge on both sides. Nothing in the cell is centred. */
.cell__foot{
  display:flex; align-items:baseline; justify-content:space-between; gap:.75rem;
  margin-block-start:auto; padding-block-start:.75rem;
}
/* Fields 6, 7 and 8 travel together on the left of that row, so they are one column and the
   reference stays baseline-aligned with the price and not with the shipped line. */
.cell__prices{ min-inline-size:0; }
.cell__price{
  font-size:var(--t-cell-price); line-height:var(--lh-cell-price);
  letter-spacing:var(--ls-cell-price); font-weight:700;
  color:var(--text-heading);            /* the price is never green */
  font-variant-numeric:tabular-nums;
}
.cell__pricelab{
  margin-block-start:.25rem;
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; color:var(--text-muted);
}
/* .cell__spec, .cell__shipped and .cell__ref went with the things they styled on 2026-09-09:
   the specification line repeated the title, the second price moved to the item page, and the
   reference is no longer shown to customers at all. */
.cell__group{
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; font-weight:600; color:var(--accent-on-dark); white-space:nowrap;
}

/* A group cell prints a range opener rather than a single price, and "From C$1,235" must not
   break across 2 lines in a 336px cell. */
.cell--group .cell__price{ white-space:nowrap; }

/* THE PRICE USED TO PRINT ON TOP OF THE REFERENCE. .cell__foot is a flex row holding the
   price column and the reference. .cell__prices carries min-inline-size:0, so it is the only
   thing in that row allowed to collapse, and .cell__ref carries white-space:nowrap, so it
   refuses to. Once the row is too narrow for both, the price column is squeezed to nothing
   and the price text paints straight over the reference. At 375px the price box measured
   3.9px wide with "C$2,660" laid out 78.1px inside it, overlapping the reference by 62.2px.

   The trigger is the CELL and not the viewport, because cell width does not follow viewport
   width: the sheet steps from 2 columns to 3 at 700px and to 4 at 1040px, and on the shop
   page the type rail takes 240px off the sheet before the columns are divided. Measured
   across the range before this rule, cards overprinting out of those rendered: 6 of 6 at
   375px, 16 of 24 at 700px, none from 820 to 1039px, 24 of 24 at 1040px, 24 of 24 at 1100px,
   16 of 24 at 1200px, 2 of 24 at 1280px, none at 1300px and above. So this was never a phone
   fault: the worst of it was on a laptop. Three disjoint viewport bands would express that
   badly and would break again the next time a column count moves. A container query says the
   real rule once: when this card is too narrow to hold both on one line, stack them.

   A container query measures the container's CONTENT box, so the threshold is stated in those
   terms. One row needs 212px of content: a 78px price, the 12px gap, and the 122px reference,
   measured against the widest price the shop currently holds. 230px is that plus enough slack
   for a price one digit longer than anything in stock today, and it is the point at which the
   row is kept rather than merely survived: at 202px of content the price clears the reference
   by under a pixel, which is not a layout, it is a coincidence. */
.cell{ container-type:inline-size; }
@container (max-width:230px){
  .cell__foot{ flex-direction:column; align-items:flex-start; }
}

/* The wide prose cell. It is prose inside a grid of objects, so it gets 1.5rem. */
.cell--fact{
  padding:1.5rem;
  background:var(--surface-default);
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}
.fact__h{
  font-size:var(--t-h4); line-height:var(--lh-h4); letter-spacing:var(--ls-h4);
  font-weight:700; text-transform:uppercase; color:var(--text-heading);
  margin-block-end:.75rem;
}
.fact__p{ font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted); }
.fact__p + .fact__p{ margin-block-start:.75rem; }

/* The 374px board crop, used inside a wide fact cell on the home page and at the head of
   /testing/. Capped at its own intrinsic width so it is never upscaled. */
.floorfig,
.testing__fig{ margin:0 0 1.5rem; max-inline-size:374px; }

/* ---------------------------------------------------------------------------
   THE GREEN WASH ON PHOTOGRAPHS OF THE PLACE
   Ruben's choice, 2026-09-08, from three options rendered side by side. I argued
   against it and he overruled me, which is his call; the evidence was shown and
   the decision is his.

   WHERE IT DOES NOT GO, and this is deliberate: the product photographs on the
   cards and the item pages. Those are white-background shots of the actual
   machine somebody is about to buy, and a green cast over one misrepresents the
   colour of goods for sale. That is a different thing from styling a room, and
   it is the kind of thing the Competition Bureau takes an interest in. So the
   wash covers the building and the counter and stops there.

   Applied with ::after rather than a filter so the photograph keeps its own
   detail underneath, and multiply so light areas take the tint while the dark
   ones stay dark. Both figures already needed position:relative for it.
   --------------------------------------------------------------------------- */
.storefig img{ filter:saturate(.82) contrast(1.06); }

/* A flat tint, not a gradient. The first version faded down the frame and the acceptance
   test rejected it: this site bans gradients outright, and that rule is older and better
   argued than my wash. Flat is also the honest version, because a fade implies a light
   source that is not in the photograph. */

/* The building and counter figures stack image then caption, so the figure is made a grid
   and the wash is placed in the image's own cell. Covering the whole figure would tint the
   caption too. */
.storefig{ display:grid; grid-template-rows:auto auto; }
.storefig img{ grid-area:1 / 1; }
.storefig figcaption{ grid-area:2 / 1; }
.storefig::after{
  content:""; grid-area:1 / 1; pointer-events:none;
  background:rgba(var(--accent-rgb), .20);
  mix-blend-mode:multiply;
}

/* The hero had its own copy of this wash while it carried a photograph. It carries proof
   cells now, so the rule went with the picture on 2026-09-09. */

/* The first photographs of 35 Progress Drive itself. Full measure rather than the 374px the
   two above use, because the point of these is what the building and the counter actually
   look like, and a thumbnail of a building tells a visitor nothing. */
.storefig{ margin:1.25rem 0 1.75rem; }
.storefig img{
  display:block; inline-size:100%; block-size:auto;
  border-radius:var(--r-1);
}
.storefig figcaption{
  margin-block-start:.5rem;
  color:var(--text-muted); font-size:var(--t-small); line-height:var(--lh-small);
}

/* The 7 categories as real links, ordered by count. A row is a target, and a target is 44px
   minimum, which is where 1.25rem of block padding comes from. */
.shelf{ margin-block:1rem; }
.shelf__row{ border-block-end:1px solid var(--hairline); }
.shelf__row a{
  display:flex; align-items:baseline; justify-content:space-between; gap:1rem;
  padding-block:1.25rem;
  color:var(--text-heading); text-decoration:none;
  font-size:var(--t-small); font-weight:600;
  transition:background-color 120ms linear;
}
.shelf__row a:hover{ background:var(--surface-raised); color:var(--text-heading); }
.shelf__name{ min-inline-size:0; }
.shelf__n{ font-variant-numeric:tabular-nums; flex:0 0 auto; }

/* The last cell of the home sheet, spanning the full row. It is the only cell in the sheet
   with no photograph and no price, so it says what it is and gets out of the way. */
.cell--seeall{ background:var(--surface-default); }
.cell--seeall a{
  display:inline-flex; align-items:center; min-block-size:44px;
  color:var(--text-heading); text-decoration:none;
  font-size:var(--t-small); font-weight:700;
  transition:color 120ms linear;
}
.cell--seeall a:hover{ color:var(--accent-on-dark); }

/* List density. 56px rows, 24 to a laptop screen, which is why cables are readable here and
   not in a photograph grid. */
.sheet--list .cell{
  flex-direction:row; align-items:center; gap:1rem;
  padding-block:.375rem; min-block-size:56px;
  contain-intrinsic-size:auto 56px;
}
.sheet--list .cell__plinth{
  flex:0 0 44px; inline-size:44px; block-size:44px;
  aspect-ratio:1 / 1; padding:3px;
  --plinth-c:var(--plinth-small);
}
.sheet--list .cell:hover .cell__plinth{ --plinth-c:var(--plinth); }
.sheet--list .cell__name{
  margin:0; flex:1 1 auto;
  font-size:var(--t-list-name); line-height:var(--lh-list-name);
  -webkit-line-clamp:1; line-clamp:1;
}
.sheet--list .cell__pricelab{ display:none; }
.sheet--list .cell__cond{ margin:0; flex:0 0 auto; }
.sheet--list .cell__foot{
  margin:0; padding:0; flex:0 0 auto; gap:1rem; align-items:baseline;
}
/* Prose does not belong in a table. */
.sheet--list .cell--fact,
.sheet--list .cell--photo{ display:none; }

/* ---------------------------------------------------------------------------
   HOME, THE MODEL GROUP STRIP
   --------------------------------------------------------------------------- */

/* The home page's <main> holds no .wrap of its own, because the hero and the sheet are both
   full bleed by design. The 3 bands that follow the sheet are prose and controls, so each one
   carries the wrap measure and the gutter itself. */
.groups{
  max-inline-size:var(--wrap); margin-inline:auto;
  padding:4rem var(--gutter) 0;
}
.groups__h{ margin-block-end:.75rem; }
.groups__p{
  font-size:var(--t-lede); line-height:var(--lh-lede); color:var(--text-body);
  max-inline-size:68ch;
}
.sheet--groups{ margin-block-start:1.5rem; }

/* ---------------------------------------------------------------------------
   HOME, THE eBAY REPUTATION STRIP
   --------------------------------------------------------------------------- */

/* No stars, no avatars, no quotes, and none of it marked up as a rating. It is 2 sentences
   and the date they were read, which is the whole of what the ground truth supports. */
.rep{
  max-inline-size:var(--wrap); margin-inline:auto;
  padding:4rem var(--gutter) 0;
}
.rep__h{
  font-size:var(--t-h3); line-height:var(--lh-h3); letter-spacing:var(--ls-h3);
  font-weight:700; color:var(--text-heading);
  font-variant-numeric:tabular-nums;
  max-inline-size:34ch;
}
.rep__p{
  margin-block-start:.75rem;
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
  max-inline-size:68ch;
}

/* THE eBAY SEAL, in the opening block. Ruben chose this shape on 2026-09-09 out of three:
   a credential the size of the audit badges beside it, not a banner. The eBay wordmark is
   NOT reproduced, here or anywhere: a logo drawn from memory is a worse fault than no logo,
   so the four brand colours run across the top edge instead and the words say the rest. */
.seal{
  display:block; position:relative;
  padding:1.6rem 1.5rem 1.4rem;
  color:var(--text-body); text-decoration:none;
  font-size:var(--t-small); line-height:var(--lh-small);
  transition:background-color 120ms linear;
}
.seal:hover{ background:var(--surface-raised); }
.seal b{
  display:block; font-size:var(--t-h3); line-height:var(--lh-h3); font-weight:500;
  color:var(--text-heading); font-variant-numeric:tabular-nums;
}
.seal small{
  display:block; font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow);
  letter-spacing:var(--ls-eyebrow); text-transform:uppercase; color:var(--text-muted);
}
/* Four flat blocks and not a gradient. accept.py check 67 bans gradients from this stylesheet
   and it is right to: every surface here is flat. Four solid fills say the same thing and keep
   the same discipline. */
.seal__flag{
  position:absolute; inset-inline:0; inset-block-start:0; block-size:5px; display:flex;
}
.seal__flag span{ flex:1 1 25%; }
.seal__flag span:nth-child(1){ background:var(--ebay-red); }
.seal__flag span:nth-child(2){ background:var(--ebay-blue); }
.seal__flag span:nth-child(3){ background:var(--ebay-yellow); }
.seal__flag span:nth-child(4){ background:var(--ebay-green); }

/* ---------------------------------------------------------------------------
   HOME, THE QUIET BUSINESS CHIP
   --------------------------------------------------------------------------- */

/* One line, one control, last on the page. It carries 2 real figures, so it is not an eyebrow
   pill with nothing in it. */
.bizchip{
  max-inline-size:var(--wrap); margin-inline:auto;
  padding:4rem var(--gutter) 0;
}
.bizchip a{
  display:block; max-inline-size:68ch;
  padding:1.25rem;
  border:1px solid var(--hairline-strong); border-radius:var(--r-1);
  color:var(--text-body); text-decoration:none;
  font-size:var(--t-small); line-height:var(--lh-small);
  font-variant-numeric:tabular-nums;
  transition:background-color 120ms linear, border-color 120ms linear;
}
.bizchip a:hover{
  background:var(--surface-raised); border-color:var(--edge); color:var(--text-body);
}

/* ---------------------------------------------------------------------------
   4.6 FILTER CHIPS
   --------------------------------------------------------------------------- */

/* The flex container has to be the list, not the nav. The markup is nav.chips > ul > li > a,
   so putting the row on .chips gives it exactly one flex child, the block level ul, and the
   chips stack into a vertical staircase beside an empty page. They fit one line with room
   to spare. */
.chips{ display:block; }
.chips ul{
  display:flex; flex-wrap:wrap; gap:.5rem;
  list-style:none; margin:0; padding:0;
}
.chip{
  display:inline-flex; align-items:center;
  min-block-size:44px; padding-inline:.875rem;
  background:var(--surface-default);
  border:1px solid var(--edge); border-radius:var(--r-1);
  color:var(--text-body); text-decoration:none;
  font-size:var(--t-small); line-height:var(--lh-small); white-space:nowrap;
  transition:background-color 120ms linear;
}
.chip:hover{ background:var(--surface-raised); color:var(--text-body); }
/* Selected is 2 channels, a fill and a 3px rule, because colour alone fails SC 1.4.1. The
   mint fill with the page colour as its label measures 8.56. White on this mint is 2.06 and
   is banned, so the label is dark and stays dark. */
.chip[aria-current="page"]{
  background:var(--focus-ring);
  border-color:var(--focus-ring);
  color:var(--surface-page);
  font-weight:700;
  box-shadow:inset 0 -3px 0 var(--surface-page);
}
.chip[aria-current="page"]:hover{ background:var(--focus-ring); color:var(--surface-page); }
/* The count needs its own space and its own weight, or every chip reads as "Gaming12". */
.chip__n{
  font-variant-numeric:tabular-nums;
  margin-inline-start:.4em; color:var(--text-muted); font-weight:400;
}
.chip[aria-current="page"] .chip__n{ color:var(--surface-page); }

/* ---------------------------------------------------------------------------
   SHOP AND BUSINESS PAGE HEADS
   --------------------------------------------------------------------------- */

/* Both of these headlines carry a live count written by the nightly script, so both set their
   figures on the tabular axis and neither is ever typed by hand. */
.shop__h1,
.biz__h1{
  font-variant-numeric:tabular-nums;
  max-inline-size:28ch;
  margin-block-end:.75rem;
}
.shop__sub{
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
  max-inline-size:68ch;
  margin-block-end:1.5rem;
}
.biz__lede,
.model__lede{
  font-size:var(--t-lede); line-height:var(--lh-lede); letter-spacing:var(--ls-lede);
  color:var(--text-body); max-inline-size:68ch;
}
.biz__lede{ margin-block-end:1.5rem; }
.model__lede + .model__lede{ margin-block-start:1rem; }

/* ---------------------------------------------------------------------------
   4.7 SEARCH, 4.8 SORT AND DENSITY
   --------------------------------------------------------------------------- */

.controls{
  display:flex; flex-wrap:wrap; align-items:flex-end; gap:1rem;
  padding-block:1rem;
  border-block-end:1px solid var(--hairline-strong);
}

.sortwrap{ display:flex; flex-direction:column; gap:.25rem; }
.sortwrap > label{
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; color:var(--text-muted);
}

/* The one sentence that keeps the new default order honest. It names the things that decide
   the order and says outright that no money and no measurement sit behind it, which is what
   answers the general impression the representation leaves and not only its literal words.
   It takes a whole flex line under the controls, and it is always visible: a disclosure a
   shopper has to hover to find is not a disclosure. */
.sortnote{
  flex:1 0 100%;
  font-size:var(--t-caption); line-height:var(--lh-caption);
  color:var(--text-muted);
}

.search{ display:flex; align-items:center; gap:.5rem; flex:1 1 16rem; }
input[type="search"],
input[type="text"],
select{
  min-block-size:44px;
  padding:.5rem .75rem;
  background:var(--surface-raised);
  color:var(--text-body);
  border:1px solid var(--edge);          /* 3.39 on the raised ground, clears SC 1.4.11 */
  border-radius:var(--r-1);
  font-size:var(--t-small); line-height:var(--lh-small);
  transition:border-color 120ms linear;
}
input[type="search"]{ inline-size:100%; }
input[type="search"]:hover,select:hover{ border-color:var(--text-muted); }

/* The native cancel glyph is a sub-24px target sitting next to a real 44px Clear button, so
   only the real button ships. */
input[type="search"]::-webkit-search-cancel-button{ display:none; }

/* Clear and Search are the same control class of thing: a 44px labelled button beside the
   field. Search is the no-script submit path and must look like a real control, because with
   the script off it is the only way to run a query. */
.search__clear,
.search__go{
  display:inline-flex; align-items:center;
  min-block-size:44px; min-inline-size:44px; padding-inline:.75rem;
  background:var(--surface-default); color:var(--text-body);
  border:1px solid var(--edge); border-radius:var(--r-1);
  font-size:var(--t-small);
  transition:background-color 120ms linear, border-color 120ms linear;
}
.search__clear:hover,
.search__go:hover{ background:var(--surface-raised); }

/* The 2px inner padding keeps the pressed fill off the group's own boundary, so --edge always
   sits against --surface-default at 3.79 and never against --surface-pressed at 2.92. */
.density{
  display:inline-flex; align-items:stretch; gap:0;
  padding:2px;
  background:var(--surface-default);
  border:1px solid var(--edge); border-radius:var(--r-1);
}
.density__btn{
  display:inline-flex; align-items:center;
  min-block-size:44px; min-inline-size:44px; padding-inline:.75rem;
  background:var(--surface-default); color:var(--text-body);
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; font-weight:600;
  transition:background-color 120ms linear;
}
.density__btn + .density__btn{ border-inline-start:1px solid var(--hairline-strong); }
.density__btn:hover{ background:var(--surface-raised); }
/* A fill plus weight 700, so the pressed state survives a greyscale filter. */
.density__btn[aria-pressed="true"]{
  background:var(--surface-pressed); color:var(--text-heading); font-weight:700;
}

/* The single permanent status region. Visible on the pages that filter, visually hidden
   elsewhere, and it is always the same element in the same place. */
.resultline{
  font-size:var(--t-small); line-height:var(--lh-small);
  color:var(--text-muted); font-variant-numeric:tabular-nums;
  padding-block:.75rem;
}

.empty{
  padding-block:3rem;
  font-size:var(--t-body); line-height:var(--lh-body); color:var(--text-muted);
  max-inline-size:56ch;
}
.empty__h{
  font-size:var(--t-h4); line-height:var(--lh-h4); letter-spacing:var(--ls-h4);
  font-weight:700; color:var(--text-heading);
  margin-block-end:.75rem;
}

/* ---------------------------------------------------------------------------
   4.9 TYPE FACET RAIL
   --------------------------------------------------------------------------- */

.facet legend{
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; font-weight:600; color:var(--text-muted);
  padding-block-end:.5rem;
}
.facet__list li{
  display:flex; align-items:center; gap:.5rem;
  min-block-size:44px;
  font-size:var(--t-small); line-height:var(--lh-small);
}
.facet__list label{ flex:1 1 auto; cursor:pointer; }
.facet__n{ color:var(--text-muted); font-variant-numeric:tabular-nums; margin-inline-start:.4em; }

/* The tail of the 47 types, behind a native disclosure. summary is focusable and toggles on
   Enter and Space on its own, so neither behaviour is re-implemented. */
.facet__more{ margin-block-start:.5rem; }

/* The accent is the heading colour and not the brand green on purpose. A rail can hold 8
   checked boxes at once, and 8 green marks would blow the 2-green budget on its own. A near
   white box with a dark tick reads as checked, and the boundary measures 11.78 on the raised
   ground. */
.facet input[type="checkbox"]{
  inline-size:1.125rem; block-size:1.125rem; flex:0 0 auto;
  accent-color:var(--text-heading);
  cursor:pointer;
}

summary{
  cursor:pointer;
  padding-block:.75rem;
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-heading);
}
details > summary{ list-style-position:inside; }

@media (max-width:1039px){
  .shop__rail{ border-block-end:1px solid var(--hairline-strong); padding-block-end:1rem; }
}

/* 4.9a The facet disclosure. Above 1040px the rail is a sticky column beside the grid and the
   facet is simply open, so the summary is not shown and the fieldset's own legend is the
   visible label. Below 1040px the rail sits above the grid and the summary is the visible
   label and the control, so the legend goes to the screen reader only and the fieldset keeps
   its accessible name at every width. The markup ships with the disclosure open: closing it
   is site.js's job, so no JavaScript means today's page and never a filter that will not
   open. */
.facetbox{ border:0; padding:0; }
/* Left as the default list-item display on purpose. display:flex on a summary removes the
   native disclosure triangle in Chrome, and a row that opens with no marker on it gives a
   phone no clue that there is anything to open. */
.facetbox__sum{
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; font-weight:600; color:var(--text-heading);
  min-block-size:44px;
}
.facetbox__n{
  color:var(--text-muted); font-variant-numeric:tabular-nums; margin-inline-start:.4em;
}

/* Hidden only while the disclosure is OPEN. Hiding it unconditionally was a hole: site.js
   closes the facet under 1040px, and once a shopper has opened and closed it themselves
   site.js stops managing it, so widening the window past 1040px arrived at a CLOSED facet
   with its summary display:none. That left all 47 type checkboxes unreachable by pointer and
   by keyboard, a 240px rail column of empty ground still reserved beside the grid, and any
   filter the shopper had ticked stuck on with no control left to clear it. Maximising a
   window, docking a second monitor or zooming back out from 150% all reach it. Tying the
   rule to [open] keeps the desktop rail exactly as it was and always leaves a way back in. */
@media (min-width:1040px){
  .facetbox[open] > .facetbox__sum{ display:none; }
}
@media (max-width:1039px){
  .facet__legend{
    position:absolute; inline-size:1px; block-size:1px;
    padding:0; margin:-1px; overflow:hidden;
    clip-path:inset(50%); white-space:nowrap; border:0;
  }
}

/* ---------------------------------------------------------------------------
   BUTTONS
   --------------------------------------------------------------------------- */

/* Rule 38, made impossible by construction. Every descendant of anything sitting on the green
   fill takes white explicitly, so nothing can inherit --text-heading and land on 4.13.
   #FFFFFF on #0C8566 measures 4.60 and is the only permitted text on the green. */
.btn-primary,.btn-primary *,
.btn-primary:hover,.btn-primary:focus,.btn-primary:active,
.callstrip,.callstrip *,
.callstrip a:hover,.callstrip a:focus,
.skip:focus,.skip:focus *{ color:#FFFFFF; }

/* column-gap, not the space in the markup: this is a flex container, so "Call" and the
   .callnum span are 2 flex items and the whitespace between them is thrown away. Without the
   gap the button reads "Call(705)". */
.btn-primary{
  display:flex; align-items:center; column-gap:.3em;
  inline-size:100%; min-block-size:52px; padding-inline:1rem;
  background:var(--accent-fill);
  border:1px solid var(--accent-fill);
  border-radius:var(--r-1);
  font-size:var(--t-body); line-height:var(--lh-small); font-weight:700;
  text-decoration:none;
  font-variant-numeric:tabular-nums;
  transition:background-color 120ms linear, border-color 120ms linear;
}
/* The hover has to be a border change and not a darker green, because a second green is not a
   token and rule 42 forbids inventing one. White on the green is already a measured pair. */
.btn-primary:hover{ border-color:#FFFFFF; }
.btn-primary:active{ border-color:var(--surface-page); }

/* The outlined control. The reveal button is the same object under the name the generator and
   the script both give it, so it is named here rather than styled twice. */
.btn-outline,
.reveal{
  display:inline-flex; align-items:center;
  min-block-size:52px; padding-inline:1rem;
  background:transparent;
  border:1px solid var(--hairline-strong);
  border-radius:var(--r-1);
  color:var(--text-heading); text-decoration:none;
  font-size:var(--t-small); font-weight:600;
  transition:background-color 120ms linear, border-color 120ms linear;
}
.btn-outline:hover,
.reveal:hover{
  background:var(--surface-raised); border-color:var(--edge); color:var(--text-heading);
}

/* aria-disabled and never disabled, so the reveal control keeps focus on its final press. */
[aria-disabled="true"]{ color:var(--text-muted); cursor:default; }

/* ---------------------------------------------------------------------------
   4.10 REVEAL AND PAGINATION
   --------------------------------------------------------------------------- */

.revealwrap{ padding-block:2rem; }
/* On the last press the button is replaced by this paragraph, which takes the focus, so focus
   never falls to the body. */
.reveal__done{
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
  font-variant-numeric:tabular-nums;
}

.pager{ padding-block:2rem; }
.pager ul{ display:flex; flex-wrap:wrap; align-items:center; gap:.25rem; }
/* 1.25rem of inline padding puts a single digit over 44px without centring anything. */
.pager a{
  display:inline-flex; align-items:center;
  min-block-size:44px; padding-inline:1.25rem;
  border:1px solid var(--edge); border-radius:var(--r-1);
  color:var(--text-body); text-decoration:none;
  font-size:var(--t-small); font-variant-numeric:tabular-nums;
  transition:background-color 120ms linear, border-color 120ms linear;
}
.pager a:hover{ background:var(--surface-raised); color:var(--text-body); }
.pager a[aria-current="page"]{
  background:var(--surface-pressed); color:var(--text-heading); font-weight:700;
  border-color:var(--hairline-strong);
}
.pager__pos{
  font-size:var(--t-small); color:var(--text-muted); font-variant-numeric:tabular-nums;
  padding-block-end:.75rem;
}

/* ---------------------------------------------------------------------------
   BREADCRUMB
   --------------------------------------------------------------------------- */

.crumb{ padding-block:1rem; font-size:var(--t-small); line-height:var(--lh-small); }
.crumb ol{ display:flex; flex-wrap:wrap; align-items:center; gap:.5rem; }
.crumb li{ color:var(--text-muted); }
.crumb li + li::before{ content:"/"; margin-inline-end:.5rem; color:var(--text-muted); }
.crumb a{ color:var(--text-muted); }
.crumb a:hover{ color:var(--accent-on-dark); }

/* ---------------------------------------------------------------------------
   4.11 ITEM PAGE, MEDIA COLUMN
   --------------------------------------------------------------------------- */

/* The mirror holds 1 file per item, 333px to 500px wide, and there is no second width to
   offer. The media column runs to roughly 800px at --wrap, so an uncapped plinth would
   upscale every photograph by half again and soften it. The cap goes on the figure so the
   caption travels with the picture. */
.item__fig{ margin:0; max-inline-size:560px; }
/* Same plinth as the sheet cell with more room around the product, because this is the one
   photograph on the page and the first thing 56% of visitors go to. */
.item__plinth{ padding:8%; }
.item__cap{
  padding-block:.75rem;
  font-size:var(--t-caption); line-height:var(--lh-caption); color:var(--text-muted);
}

/* ---------------------------------------------------------------------------
   4.11 ITEM PAGE, BUY MODULE
   --------------------------------------------------------------------------- */

.item__eyebrow{
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; color:var(--text-muted);
}
.item__eyebrow a{ color:inherit; text-decoration:none; }
.item__eyebrow a:hover{ color:var(--accent-on-dark); }

.item__h1{
  margin-block:.5rem .75rem;
  font-size:var(--t-h3); line-height:var(--lh-h3); letter-spacing:var(--ls-h3);
  font-weight:600; color:var(--text-heading);
  text-transform:none;                 /* sentence case, never uppercase */
}
/* Printed only when the title already carries the code. Never derived, never invented. */
.item__model{ margin-block-end:.75rem; color:var(--text-muted); }

.price{
  font-size:var(--t-price); line-height:var(--lh-price); letter-spacing:var(--ls-price);
  font-weight:700; color:var(--text-heading);   /* never green */
  font-variant-numeric:tabular-nums;
}
.price__lab{
  margin-block-start:.25rem;
  font-size:var(--t-caption); line-height:var(--lh-caption); color:var(--text-muted);
}
.price__two{
  margin-block-start:.75rem;
  font-size:var(--t-price-2); line-height:var(--lh-price-2);
  font-weight:500; color:var(--text-muted); font-variant-numeric:tabular-nums;
}
/* The difference is stated in dollars as plain text. No badge, no strikethrough, no
   percentage, ever: on one-of-a-kind stock neither ordinary-price test can be met. */
.price__diff{
  margin-block-start:.5rem;
  font-size:var(--t-caption); line-height:var(--lh-caption); color:var(--text-muted);
}

.factband{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:.75rem;
  margin-block:1.5rem;
  padding-block:.75rem; min-block-size:48px;
  border-block:1px solid var(--hairline);
}
/* A reference is 15 characters of monospace in a third of a 5-column track, so the cell has to
   be allowed to be narrower than its content rather than widening the band. */
.factband__cell{ min-inline-size:0; }
.factband dt{
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; color:var(--text-muted);
}
.factband dd{
  margin:0; font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-body);
}

/* One of the approved scarcity lines. It is a fact about the stock, not a countdown. */
.noholds{
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}

/* Field 10, the same clock as the rail read a second time where it is load-bearing.
   It carries no state colour of its own: 3.6 counts the rail's dot as 1 of the 2 greens a
   viewport may hold and the call button's fill is the other, and 3.7 confines --state-closed
   to the rail and nowhere else, ever. The words themselves change, so the state is not being
   carried by colour in the first place. */
.cstate{
  display:flex; align-items:center; gap:.5rem;
  min-block-size:40px;
  margin-block-start:1rem;
  border-block-end:1px solid var(--hairline);
  font-size:var(--t-caption); line-height:var(--lh-caption); color:var(--text-muted);
}
.cstate__dot{
  flex:0 0 auto; align-self:center;
  inline-size:8px; block-size:8px; border-radius:50%;
  background:var(--text-muted);
}

.item__cta{ margin-block-start:1.5rem; }
.item__who{
  margin-block-start:.5rem;
  font-size:var(--t-caption); line-height:var(--lh-caption); color:var(--text-muted);
}
.item__cta2{ margin-block-start:.75rem; }

/* Printed only on a unit that belongs to a group. It says the units are separate machines,
   which is the one thing a duplicate listing makes people doubt. */
.grouped{
  margin-block-start:1.5rem;
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}

/* No icon, no coloured box. It is the best 40 words in the project and it does not need one. */
.script{
  margin-block-start:2rem;
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}
.script p + p{ margin-block-start:.75rem; }

.wipe{ margin-block-start:2rem; font-size:var(--t-small); line-height:var(--lh-small);
       color:var(--text-muted); }
.provenance{ margin-block-start:1.5rem; font-size:var(--t-small); line-height:var(--lh-small);
             color:var(--text-muted); }

/* The 3 module headings in the buy column, set alike so the column reads as one object. */
.script__h,
.wipe__h,
.spec__h{
  font-size:var(--t-h4); line-height:var(--lh-h4); letter-spacing:var(--ls-h4);
  font-weight:700; color:var(--text-heading);
  margin-block-end:.75rem;
}
.spec__h{ margin-block-start:2rem; }

/* The specification list is a table, and a table is read down a column, so 0.5rem. */
.spec{ border-block-start:1px solid var(--hairline); }
.spec__row{
  display:flex; align-items:baseline; justify-content:space-between; gap:1rem;
  padding-block:.5rem;
  border-block-end:1px solid var(--hairline);
}
.spec dt{ font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted); }
.spec dd{
  margin:0; text-align:end;
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-body);
  font-variant-numeric:tabular-nums;     /* digits stack down the column */
}

/* SC 3.2.6. The address, the hours and the phone close every one of the 17 page types in the
   same relative position. */
.addrstrip{
  margin-block-start:2rem; padding-block-start:1rem;
  border-block-start:1px solid var(--hairline);
  font-size:var(--t-caption); line-height:var(--lh-caption); color:var(--text-muted);
}

/* ---------------------------------------------------------------------------
   4.12 THE THREE QUESTIONS
   --------------------------------------------------------------------------- */

/* No red, no amber, no warning icon, no border box. Colouring an unknown as a warning says
   we are embarrassed by it, and every row here promises an answer on the phone. */
.ask{ margin-block-start:2rem; }
.ask__h{
  font-size:var(--t-h4); line-height:var(--lh-h4); letter-spacing:var(--ls-h4);
  font-weight:600; color:var(--text-heading); margin-block-end:.75rem;
}
.ask__list{ border-block-start:1px solid var(--hairline); }
.ask__row{ padding-block:.75rem; border-block-end:1px solid var(--hairline); }
.ask__row dt{
  font-size:var(--t-small); line-height:var(--lh-small); font-weight:600;
  color:var(--text-body);
}
.ask__row dd{
  margin:0; font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}

/* ---------------------------------------------------------------------------
   MOBILE ACTION BAR, 4.11
   --------------------------------------------------------------------------- */

.mbar{ display:none; }

@media (max-width:900px){
  /* Exactly 2 controls, CALL and DIRECTIONS, or 1 control while the maps URL is unconfirmed.
     Never 3. */
  .mbar{
    position:fixed; inset-inline:0; inset-block-end:0; z-index:150;
    display:flex; align-items:center; gap:.5rem;
    min-block-size:56px;
    /* 0.375rem above and below a 44px target is exactly 56px, which is the height 4.11 asks
       for and the height reserved at the foot of the page. It is the same pair of numbers as
       the 56px list density row in 3.9. */
    padding:.375rem var(--gutter);
    padding-block-end:calc(.375rem + env(safe-area-inset-bottom));
  }
  .mbar > *{ flex:1 1 0; }
  /* .btn-primary is 52px, which does not fit a 56px bar once the bar's own padding is
     counted, so the call control drops to the 44px minimum target inside the bar. It also
     takes the larger share of the width: Directions is one short word, this one carries a
     phone number with an extension on it, and at an even split the number broke across 2
     lines in the middle of the digits. */
  .mbar__call{ min-block-size:44px; flex-grow:1.6; }
  /* Buy leads on an item page and Call keeps the larger share, because Call carries a phone
     number with an extension on it and Buy is three short words. */
  .mbar__buy{ min-block-size:44px; }

  /* The bar is fixed, so the last thing on an item page needs somewhere to sit. 56px is the
     floor from 4.11; site.js publishes the bar's measured height, which is what matters at
     320px and at 200% zoom where the bar wraps, and the larger of the 2 wins. Keep the max():
     if the script never runs, the fallback still reserves the full 56px. */
  .page-item{
    padding-block-end:calc(max(56px,var(--actionbar-h,56px)) + env(safe-area-inset-bottom));
  }
}

/* On a narrow phone the 2 controls plus a phone number that is not allowed to break do not
   fit the 1rem paddings, and Directions was clipped at the gutter. The bar tightens rather
   than dropping a control or splitting a digit. Measured: the call control needs 233px and
   Directions 93px at these paddings, so 360px is the narrowest screen that still gets the
   number on 1 line. */
@media (max-width:400px){
  .mbar{ padding-inline:.5rem; gap:.25rem; }
  .mbar > *{ padding-inline:.75rem; }
}
/* At 320px even that does not fit, so the call control stacks its own 2 words. The number
   still never breaks; the line falls after the word Call. */
@media (max-width:340px){
  .mbar__call{ flex-wrap:wrap; justify-content:center; }
}

/* ---------------------------------------------------------------------------
   TABLES: sibling units, and the business list
   --------------------------------------------------------------------------- */

.tablewrap{ overflow-x:auto; margin-block:1.5rem; }
table caption{
  text-align:start; padding-block:.75rem;
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}
th{
  text-align:start; padding-block:.5rem; padding-inline-end:1rem;
  border-block-end:1px solid var(--hairline-strong);
  font-size:var(--t-eyebrow); line-height:var(--lh-eyebrow); letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase; font-weight:600; color:var(--text-muted);
  white-space:nowrap;
}
td{
  padding-block:.75rem; padding-inline-end:1rem;
  border-block-end:1px solid var(--hairline);
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-body);
}
tbody tr{ transition:background-color 120ms linear; }
tbody tr:hover{ background:var(--surface-raised); }
/* Money reads down a column, so it is set on the trailing edge and on the tabular axis. */
td.num{ text-align:end; font-variant-numeric:tabular-nums; }
td.mono{ white-space:nowrap; }

/* Left to itself a table will squeeze 6 columns into 320px and wrap every cell to 3 lines.
   A floor on the table sends the wrapper sideways instead, which is what it is for. */
.units{ min-inline-size:34rem; }
.biztable{ min-inline-size:48rem; }
/* 100 rows is faster to read than to look at, so the business list is dense and unphotographed. */
.biztable td{ padding-block:.5rem; }

/* ---------------------------------------------------------------------------
   MODEL GROUP PAGE
   --------------------------------------------------------------------------- */

/* A 90 character feed title plus the live unit count is 4 lines at --t-h1 on a laptop, and
   the page's job is the unit table under it, so the model headline takes the next step down. */
.model__h1{
  font-size:var(--t-h2); line-height:var(--lh-h2); letter-spacing:var(--ls-h2);
  font-variant-numeric:tabular-nums;
  max-inline-size:34ch;
}
.model__code{ margin-block:.5rem 1rem; color:var(--text-muted); }

/* One photograph per unit, on its own plinth, because these are separate machines and the
   whole point of the page is that they are not one listing repeated. */
.strip{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(160px,1fr)); gap:1rem;
  padding-block:1.5rem;
}
.strip__fig{ margin:0; }
.strip__fig figcaption{ margin-block-start:.5rem; color:var(--text-body); }

/* ---------------------------------------------------------------------------
   /testing/ CHAIN OF CUSTODY
   --------------------------------------------------------------------------- */

.chain{ border-block-start:1px solid var(--hairline); max-inline-size:68ch; }
.chain__row{
  display:flex; align-items:baseline; gap:1rem;
  padding-block:1rem;
  border-block-end:1px solid var(--hairline);
}
/* One of the 3 permitted monospace uses, and the one permitted numeral centring. */
.chain__n{
  flex:0 0 2.5rem;
  font-family:var(--ff-mono); font-size:var(--t-ref); letter-spacing:var(--ls-ref);
  text-align:center; color:var(--text-muted);
}
.chain__t{ flex:1 1 auto; font-size:var(--t-body); line-height:var(--lh-body); }

/* ---------------------------------------------------------------------------
   4.13 CERTIFICATION ROW
   --------------------------------------------------------------------------- */

/* The badges are dark ink. Two of them vanish on a dark ground, so the whole row sits on the
   plinth band with the plinth ink at 11.85. Never recoloured, never tinted, never made
   monochrome, never forced to a common width: the mismatched widths are what make them read
   as real third-party marks.

   The section sits outside .wrap in the markup, so it carries the gutter and the wrap measure
   itself rather than running its text into the edge of the viewport. */
.certs{
  background:var(--plinth);
  color:var(--plinth-ink);
  padding-block:3rem;
  padding-inline:var(--gutter);
}
.certs__h{
  max-inline-size:var(--wrap); margin-inline:auto;
  color:var(--plinth-ink);
  font-size:var(--t-h4); line-height:var(--lh-h4); letter-spacing:var(--ls-h4);
  font-weight:700; text-transform:uppercase;
  margin-block-end:2rem;
}
.certs__list{
  max-inline-size:var(--wrap); margin-inline:auto;
  display:flex; flex-wrap:wrap; gap:2rem;
}
.certs__item{ flex:1 1 13rem; max-inline-size:18rem; }
/* Height only. Setting a width here is the same act as normalising them. */
.certs__badge{
  block-size:44px; inline-size:auto; max-inline-size:100%;
  margin-block-end:.75rem;
}
.certs__line{
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--plinth-ink);
}
/* The lead-in of each line is bold, and the base rule paints bold text with the heading token,
   which is #F2F3F1 on a #EDEEEA band. That is the one place on this site where a colour lands
   on top of itself, so the ink is restated. */
.certs__line b,
.certs__line strong{ color:var(--plinth-ink); }

/* ---------------------------------------------------------------------------
   4.14 FOOTER
   --------------------------------------------------------------------------- */

/* 2 plain sentences. No logos, no bar. Outside .wrap in the markup, so it carries its own. */
.siblings{
  max-inline-size:var(--wrap); margin-inline:auto;
  padding:2rem var(--gutter);
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}
.siblings a{ color:var(--text-muted); }
.siblings a:hover{ color:var(--accent-on-dark); }

/* .callstrip is on the <footer> itself. Left aligned, and every colour in it is written out
   rather than inherited, because inheriting the heading token here measures 4.13. */
.callstrip{
  background:var(--accent-fill);
  padding-block:3rem;
}
.callstrip__inner{
  max-inline-size:var(--wrap); margin-inline:auto;
  padding-inline:var(--gutter);
}
.callstrip__h{
  font-size:var(--t-h4); line-height:var(--lh-h4); letter-spacing:var(--ls-h4);
  font-weight:700; text-transform:uppercase;
  margin-block-end:.75rem;
}
/* The phone number is the conversion control on this site, so it is the only thing in the
   strip at --t-h1. The hours that follow it in the same paragraph are not. */
.callstrip__tel{
  font-size:var(--t-small); line-height:var(--lh-small);
  font-variant-numeric:tabular-nums;
}
.callstrip__tel a{
  display:inline-flex; align-items:center;
  min-block-size:44px;
  font-size:var(--t-h1); line-height:var(--lh-h1); letter-spacing:var(--ls-h1);
  font-weight:800; font-variant-numeric:tabular-nums;
}
.callstrip__addr{
  margin-block-start:.75rem;
  font-size:var(--t-small); line-height:var(--lh-small);
}
.callstrip a{ text-decoration:underline; text-underline-offset:.18em; }

/* A mint ring on a green ground measures 2.23, so the ring gets a dark plate under it. */
.callstrip :focus-visible{
  outline:3px solid var(--focus-ring); outline-offset:2px;
  box-shadow:0 0 0 2px var(--surface-page);
}

/* ---------------------------------------------------------------------------
   SECTION 9. THE FACILITY DRONE PHOTOGRAPH
   --------------------------------------------------------------------------- */

/* 4 places on the whole site and never a fifth. No scrim, no duotone, no overlay, no
   parallax, and no text over it anywhere. The crops are chosen to push the trailer's own web
   address and toll-free number out of frame, so every one of them is checked at 1280, 1440,
   1920 and 21:9 before ship. */

/* Use 2, /visit/, full bleed 21:9. */
/* The directions control, or the line standing in for it while there is no maps URL to point
   at. There is never a placeholder href here. */
.visit__dir{
  margin-block:1.5rem;
  font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted);
}

/* Use 3, one wide cell in the sheet. No caption and no words on it, which is the most
   confident thing the site does. */
.cell--photo{
  padding:0; position:relative; background:var(--surface-page); isolation:isolate;
}
.cell--photo img{
  display:block; width:100%; height:100%;
  aspect-ratio:21 / 9; object-fit:cover; object-position:52% 56%;
  filter:saturate(.86) contrast(1.04);
}

/* The duotone belongs to the 374 x 374 board crops only, and never to the drone photograph.
   The house green wash measures 4.05 over the white wall and 3.10 at the worst pixels, so it
   fails as a contrast device, and it flattens the green trailer into the green building trim,
   which is the one object in the frame that identifies the company. */
.duotone{ filter:url(#edi-duotone); }
@supports not (filter: url(#edi-duotone)){ .duotone{ filter:grayscale(1) sepia(.3); } }

/* ---------------------------------------------------------------------------
   PRINT
   The whole buying model on this site is: find the machine, write the reference
   down, ring the counter, drive to Orillia and quote it. So the item page is the
   most printed page here, and until now it printed as near white ink on white
   paper at 1.11:1, because browsers do not print backgrounds by default and this
   page is light text on charcoal. Six reviewers looked at this site and none of
   them tried to print it.
   --------------------------------------------------------------------------- */

@media print{
  /* Ink on paper, not the screen palette. Set on the tokens so every component
     follows without needing its own print rule. */
  :root{
    --surface-page:#FFFFFF; --surface-default:#FFFFFF;
    --surface-raised:#FFFFFF; --surface-pressed:#FFFFFF;
    --plinth:#FFFFFF; --plinth-hover:#FFFFFF; --plinth-small:#FFFFFF;
    --text-heading:#000000; --text-body:#000000; --text-muted:#333333;
    --accent-on-dark:#000000; --plinth-ink:#000000;
    --hairline:#BBBBBB; --hairline-strong:#888888; --edge:#666666;
  }
  html,body{ background:#FFFFFF; color:#000000; }

  /* Navigation, filters and anything that only makes sense with a pointer.
     .mbar is position:fixed, and a fixed element repeats on every printed page
     and lands on top of the content, so it has to go first. */
  .skip,.reviewbar,.hdr,.rail,.drawer,.drawer__scrim,.mbar,
  .chips,.controls,.facet,.facetbox,.facetbox__sum,.search,.sortwrap,.density,
  .revealwrap,.reveal,.resultline,.pager,.callstrip,.siblings,
  .cell--photo,.floorfig,.testing__fig,.strip__fig{
    display:none !important;
  }

  /* The logo is white on transparent, so on paper it is nothing at all. Print the
     name instead, once, at the top. */
  .hdr__logo img{ display:none; }
  main::before{
    content:"EDI Electronics Outlet, 35 Progress Drive, Orillia, Ontario L3V 0T7. (705) 327-8691 ext. 27.";
    display:block; font-weight:700; font-size:11pt; margin-block-end:1rem;
    padding-block-end:.5rem; border-block-end:1px solid #888888;
  }

  /* The reference is the thing being carried to the counter, so it is the one
     item that gets bigger on paper rather than smaller. */
  .item__ref,.mono,.factband__cell dd,.units td:first-child{
    font-family:var(--ff-mono); font-weight:700;
  }
  .factband__cell dd{ font-size:12pt; }

  /* Keep what a person needs at the counter together on one sheet. */
  .price__two,.price__diff,.factband,.script,.ask,.spec,.addrstrip,.noholds{
    break-inside:avoid; page-break-inside:avoid;
  }
  .item__h1,h1,h2{ break-after:avoid; page-break-after:avoid; }

  /* One photograph is useful for recognising the machine on the shelf. The rest
     of the catalogue imagery is not worth the toner. */
  .item__plinth img{ max-width:70mm; height:auto; }
  .cell__plinth{ display:none; }

  a{ text-decoration:underline; }
  /* Print the phone number and the address a link points at, since a printed page
     cannot be clicked. Nothing else, or every reference turns into a URL. */
  .item__cta a[href^="tel:"]::after,.addrstrip a[href^="tel:"]::after{ content:""; }

  @page{ margin:14mm; }
}

/* ---------------------------------------------------------------------------
   SOLD. Reached by somebody who followed a link to a machine that has gone.
   --------------------------------------------------------------------------- */

.page-gone .wrap{ padding-block:2rem 4rem; }
.gone__h1{
  font-size:var(--t-h1); line-height:var(--lh-h1); letter-spacing:var(--ls-h1);
  font-weight:800; text-transform:uppercase; color:var(--text-heading);
  max-width:22ch; margin:0 0 1.5rem;
}
.gone__p{
  font-size:var(--t-lede); line-height:var(--lh-lede); color:var(--text-muted);
  max-width:62ch; margin:0 0 1rem;
}
.gone__p:last-of-type{ margin-block-end:2rem; }

/* ---------------------------------------------------------------------------
   PHOTO RAIL. Every photograph EDI took of this exact unit, 2 to 19 of them.
   The main photo swaps in place; each thumbnail is a real button, so the rail
   is reachable by Tab and needs no dragging (SC 2.5.7). 44px targets (SC 2.5.8).
   --------------------------------------------------------------------------- */

.thumbs{
  display:flex; flex-wrap:wrap; gap:.5rem;
  list-style:none; margin:.75rem 0 0; padding:0;
  max-inline-size:560px;
}
.thumb{
  display:block; inline-size:64px; block-size:64px; padding:0;
  background:var(--plinth-small); border:1px solid var(--hairline-strong);
  border-radius:var(--r-1); cursor:pointer; overflow:hidden; isolation:isolate;
  transition:border-color 120ms linear;
}
.thumb img{
  display:block; inline-size:100%; block-size:100%;
  object-fit:contain; mix-blend-mode:multiply;
}
.thumb:hover{ border-color:var(--edge); }
.thumb[aria-pressed="true"]{
  border-color:var(--text-heading);
  box-shadow:inset 0 0 0 1px var(--text-heading);
}
.thumb:focus-visible{ outline:3px solid var(--focus-ring); outline-offset:2px; }
.item__count{
  margin:.5rem 0 0; font-size:var(--t-caption); line-height:var(--lh-caption);
  color:var(--text-muted);
}

/* ---------------------------------------------------------------------------
   LISTING NOTES. What EDI's own technician typed into the listing for this unit.
   Quoted, not paraphrased, because it is the only per-unit description we hold.
   --------------------------------------------------------------------------- */

.notes{ margin-block:2rem 0; padding-block-start:1.5rem; border-block-start:1px solid var(--hairline-strong); }
.notes__h{ margin:0 0 .75rem; font-size:var(--t-h4); line-height:var(--lh-h4); font-weight:700; color:var(--text-heading); }
.notes__p{ margin:0 0 .75rem; font-size:var(--t-body); line-height:var(--lh-body); color:var(--text-body); white-space:pre-line; }
.notes__src{ margin:0; font-size:var(--t-caption); line-height:var(--lh-caption); color:var(--text-muted); }
.returns{ margin:1rem 0 0; font-size:var(--t-small); line-height:var(--lh-small); color:var(--text-muted); }

/* The web checkout button, second in weight to the phone. Outlined, same height. */
/* Was --checkout until 2026-09-09, when buying became the filled control and the phone
   became the outlined one. The modifier only ever meant "full width", so it is named that. */
.btn-outline--wide{ inline-size:100%; justify-content:center; }
.item__ship{ margin:.5rem 0 0; font-size:var(--t-caption); line-height:var(--lh-caption); color:var(--text-muted); }

/* The Directions control in the mobile action bar. It only exists now that the map link is
   real, so nothing had ever styled it. Same shape as the call button beside it, outlined
   rather than filled, because the phone is the primary action and the map is the second. */
.mbar__dir{
  display:inline-flex; align-items:center; justify-content:center;
  min-block-size:44px; padding-inline:1rem;
  border:1px solid var(--hairline-strong); border-radius:var(--r-1);
  background:transparent; color:var(--text-heading);
  font-size:var(--t-small); font-weight:700; text-decoration:none;
  transition:border-color 120ms linear;
}
.mbar__dir:hover{ border-color:var(--edge); }
.mbar__dir:focus-visible{ outline:3px solid var(--focus-ring); outline-offset:2px; }

/* Paying at the counter. Plain prose block on /visit/, same rhythm as the sections around it. */
.pay{ margin-block:2rem 0; padding-block-start:1.5rem; border-block-start:1px solid var(--hairline-strong); }
.pay h2{ margin:0 0 .75rem; font-size:var(--t-h4); line-height:var(--lh-h4); font-weight:700; color:var(--text-heading); }
.pay p{ margin:0; font-size:var(--t-body); line-height:var(--lh-body); color:var(--text-body); max-width:62ch; }
