/* ============================================================
   Mobile Parts Finder — Feature components
   ============================================================ */

/* ---------------- section heading ---------------- */
.sec{ margin-top:26px; }
@media (min-width:768px){ .sec{ margin-top:34px; } }
.sec__head{ display:flex; align-items:flex-end; justify-content:space-between; gap:12px; margin-bottom:14px; flex-wrap:wrap; }
.sec__title{ display:flex; align-items:center; gap:9px; }
.sec__title h2{ font-size:clamp(18px,3vw,23px); }
.sec__count{
  font-family:var(--f-mono); font-size:12px; font-weight:600; color:var(--ink-3);
  background:var(--surface-3); padding:3px 8px; border-radius:var(--r-xs);
}

/* ============================================================
   WORKSPACE — full-width 3-panel desktop dashboard
   Below --ws-bp the exact same markup collapses to the original
   single-column page: side panels hide and move into the filter
   sheet, the horizontal category row and mobile toolbar return.
   ============================================================ */
.ws{ display:block; }
.ws__body{ display:block; }
.ws__left,.ws__right{ display:none; }
.ws__center{ max-width:var(--shell); margin-inline:auto; padding-inline:var(--pad); }
.ws-only{ display:none; }

@media (min-width:1180px){
  .main.is-ws{ padding-bottom:0; }

  /* the page itself no longer scrolls — the centre column does */
  .ws{
    height:calc(100dvh - var(--topbar-h));
    display:flex; flex-direction:column;
  }

  /* the hero band is gone on desktop — its search, stats and recent searches
     now live in the compact application header (recents in the dropdown) */
  .ws__band{ display:none; }

  .ws__body{
    flex:1 1 auto; min-height:0;
    display:grid; grid-template-columns:288px minmax(0,1fr) 244px;
  }
  .ws__left,.ws__right{
    display:block; min-height:0; overflow-y:auto; overscroll-behavior:contain;
    background:var(--surface); padding:14px 13px 24px;
  }
  .ws__left{ border-right:1px solid var(--line); }
  .ws__right{ border-left:1px solid var(--line); }
  .ws__center{
    min-width:0; min-height:0; overflow-y:auto; overscroll-behavior:contain;
    max-width:none; margin:0; padding:0 20px 36px; background:var(--paper);
  }
  .ws__left::-webkit-scrollbar,
  .ws__right::-webkit-scrollbar,
  .ws__center::-webkit-scrollbar{ width:9px; }
  .ws__left::-webkit-scrollbar-thumb,
  .ws__right::-webkit-scrollbar-thumb,
  .ws__center::-webkit-scrollbar-thumb{ background:var(--line); border-radius:99px; border:2px solid transparent; background-clip:content-box; }

  /* the panels replace these on desktop */
  .ws__center .fbar{ display:none; }
  .ws-only{ display:block; }
  .ws-tools .field{ width:230px; }

  /* flatten the head wrappers so the toolbar's containing block is the
     scroll container itself — otherwise position:sticky can only travel
     as far as its short parent box and scrolls away with it */
  .ws__center .ws__head,
  .ws__center .ws__head .sec{ display:contents; }
  .ws__center .sec__head{
    position:sticky; top:0; z-index:6; margin:0 0 12px;
    padding:14px 0 10px; background:var(--paper);
    box-shadow:0 10px 12px -12px rgba(10,21,18,.35);
  }
  /* card columns follow the real column width, not the viewport width */
  .ws__center .gridcards{ grid-template-columns:repeat(auto-fill,minmax(262px,1fr)); }
}

@media (min-width:1560px){
  .ws__body{ grid-template-columns:322px minmax(0,1fr) 274px; }
  .ws__left,.ws__right{ padding:18px 16px 28px; }
  .ws__center{ padding:0 26px 40px; }
  .ws__band .shell{ padding-inline:26px; }
}

/* ---------------- panel shell (used by both sidebars and the sheet) ------- */
.panel__head{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:10px; min-height:24px; }
.panel__n{ font-family:var(--f-mono); font-size:11px; color:var(--ink-3); background:var(--surface-3); padding:2px 7px; border-radius:6px; }
.panel__clear{ display:inline-flex; align-items:center; gap:4px; font-size:11.5px; font-weight:700; color:var(--ink-3); padding:3px 7px; border-radius:6px; }
.panel__clear:hover{ background:var(--surface-3); color:var(--ink); }
.panel__clear svg{ width:12px; height:12px; }
.panel + .panel{ margin-top:16px; padding-top:16px; border-top:1px solid var(--line-soft); }

/* a category this model has no group in: still in its place, plainly off */
.ctile.is-empty{ opacity:.4; pointer-events:none; }

/* --------- LEFT: image-first category gallery, 3 up -----------------------
   The picture is the tile. Every cell is the same square, always three to a
   row — including the last real category, which used to stretch to full width
   and broke the grid's rhythm the moment Battery happened to be last. The "+"
   slot is an ordinary cell, so adding a category cannot change the shape of
   the ones already there.

   The name is not printed under the picture. Seven tiles each captioned with
   its own name and the word "groups" is a wall of text over a gallery; the
   name appears on hover, over the lower edge of its own image, and lives in
   the button's aria-label the rest of the time so nothing is hidden from a
   screen reader or a keyboard. */
.ctiles{ --cols:3; display:grid; grid-template-columns:repeat(var(--cols),minmax(0,1fr)); gap:8px; }
.ctiles--2{ --cols:2; }
.ctiles--3{ --cols:3; }
/* Below this the sidebar is not the sidebar any more — it is the filter
   sheet, where the column choice still applies but two-up is the floor. */
@media (max-width:359px){ .ctiles{ --cols:2; } }
/* White, in both themes, and flat. The card is a mount for a white-background
   photograph: a tinted ground or a drop shadow reads as grime around the part
   rather than as depth, and it is the first thing you see when you zoom in. */
.ctile{
  --c:var(--teal-500);
  position:relative; display:block; padding:4px; border-radius:var(--r-sm);
  border:1px solid var(--line); background:var(--ctile-paper); color:var(--ink);
  transition:transform .18s cubic-bezier(.2,.8,.3,1), border-color .18s;
}
.ctile:hover{ transform:translateY(-2px); border-color:var(--c); }
.ctile:focus-visible{ outline:2px solid var(--teal-600); outline-offset:2px; }

/* The image area — and the positioning context the overlay and the badge are
   measured against.

   Portrait, because the parts are portrait: a back cover, a display, a middle
   frame and a battery are all far taller than they are wide, and a square box
   spent most of its area on white to either side of them. 3:4 is measured
   against the assets — it is the ratio at which the tallest part (a display,
   0.44) still clears 88% of the height while the one wide part (a CC board,
   1.27) still fits across without being shrunk to fit a height it does not
   need. Changing it means re-running scripts/build-category-focus.py with a
   matching --aspect. */
.ctile__art{
  position:relative; display:block; width:100%; aspect-ratio:3/4;
  border-radius:9px; overflow:hidden;
}

/* ---- product renders (one system: category tiles + group cards) ---------- */
.pthumb{
  display:grid; place-items:center; flex:none; overflow:hidden;
  background:var(--pthumb-bg); border:1px solid var(--pthumb-line); border-radius:10px;
  box-shadow:inset 0 -6px 12px -8px rgba(10,21,18,.18);
}
.pimg{ width:100%; height:100%; display:block; object-fit:contain; }
/* Fills its cell instead of sitting as a 56px stamp in the middle of one.

   The shared .pthumb carries an inset bottom shadow, which gives a small
   thumbnail a sense of a shelf. Across a whole tile it is just a grey wash
   over the lower third of the photograph — the smudge under every part in the
   sidebar. Off here, and left alone everywhere else it is still small. */
.pthumb--tile{
  position:absolute; inset:0; width:100%; height:100%; max-width:none;
  border-radius:9px; border-color:transparent; padding:0;
  background:var(--ctile-paper); box-shadow:none;
}
/* FIT THE PART, NOT ITS CANVAS.

   The masters are square pictures of a portrait part standing in a lot of
   white — a back cover is 45% of its own canvas, a display 42%. `contain`
   fits the canvas, so those parts render under half a tile wide however big
   the tile gets; enlarging the card cannot fix it, because the card is not
   what is being fitted.

   --iw / --il / --it place the image so the PART lands centred and filling
   the box, measured per asset by scripts/build-category-focus.py. Only the
   width is set and the height is auto, so the browser derives the height from
   the file's own aspect ratio and DISTORTION IS NOT EXPRESSIBLE here. The
   overhang is the asset's white margin, clipped against a white chip, so
   nothing of the part is lost and no edge shows.

   The fallback values are plain contain behaviour, which is what a category
   with no measurement — or the drawn SVG stand-in — should get. */
/* The phone rail gets the identical treatment: its art box is 3:4 too, which
   is the ratio the focus numbers were measured for, so the same three
   variables place the part correctly at either size. */
.pthumb--tile img.pimg,
.crail__art .pthumb--rail img.pimg{
  position:absolute;
  width:var(--iw,100%); height:auto;
  /* The global img{max-width:100%} reset in styles.css caps an image at its
     container, which is exactly what has to be overridden here: the image is
     deliberately WIDER than the box so the part inside it is not. Without
     this the width silently clamps back to 100% and every part returns to
     canvas size — the same picture, and no visible error to chase. */
  max-width:none;
  left:var(--il,0); top:var(--it,0);
  object-fit:contain;
}

/* …and on the phone it is scaled by --crail-fit. The scale is DERIVED rather
   than re-measured: the focus numbers are linear in the fit factor, so
   multiplying the width by f and pushing the offsets f× further from centre is
   exactly the same fit computed at an f× inset. The part stays centred, its
   aspect ratio is still the file's own, and nothing needs regenerating. */
.crail__art .pthumb--rail img.pimg{
  width:calc(var(--iw,100%) * var(--crail-fit,1));
  left:calc(50% - (50% - var(--il,0%)) * var(--crail-fit,1));
  top:calc(50% - (50% - var(--it,0%)) * var(--crail-fit,1));
}

/* the drawn stand-in has no canvas margin to correct for */
.pthumb--tile svg.pimg,
.crail__art .pthumb--rail svg.pimg{ width:100%; height:100%; padding:8%; }
.pthumb--plate{ width:58px; height:58px; border-radius:12px; }

/* Hover reveal. Only the bottom of the picture is shaded — enough to carry a
   line of text, not a curtain over the thing the tile is for. */
.ctile__veil{
  position:absolute; left:0; right:0; bottom:0; height:56%; pointer-events:none;
  background:linear-gradient(to top, rgba(5,14,13,.88) 0%, rgba(5,14,13,.55) 42%, rgba(5,14,13,0) 100%);
  opacity:0; transition:opacity .2s ease;
}
.ctile__name{
  position:absolute; left:6px; right:6px; bottom:6px; pointer-events:none;
  font-size:10.5px; font-weight:700; line-height:1.2; letter-spacing:-.01em;
  color:#FFFFFF; text-align:left; text-shadow:0 1px 2px rgba(0,0,0,.5);
  opacity:0; transform:translateY(4px); transition:opacity .2s ease, transform .2s ease;
  overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
}
/* Two-up tiles are wide enough for the longer names to sit on one line, so
   the type grows with the card rather than staying at sidebar size. */
.ctiles--2 .ctile__name{ font-size:12px; left:8px; right:8px; bottom:7px; }
.ctiles--2 .ctile__n{ height:20px; min-width:22px; font-size:11px; }
/* EVERYTHING that is not the picture appears only under the pointer.

   Selection is deliberately NOT in this list. A selected tile shows its
   outline and nothing else: the name and the count are what you look up while
   choosing, and repeating them on the one tile you have already chosen is the
   clutter this panel is trying to lose. Hovering a selected tile still brings
   them back, because that is you asking again. */
.ctile:hover .ctile__veil,
.ctile:focus-visible .ctile__veil{ opacity:1; }
.ctile:hover .ctile__name,
.ctile:focus-visible .ctile__name{ opacity:1; transform:none; }
.ctile:hover .ctile__n,
.ctile:focus-visible .ctile__n{ opacity:1; transform:none; }

/* Without a pointer there is no hover at all, so hover-only would put the
   name and the count permanently out of reach on a phone. There they stay
   on — softer, and only where a mouse was never going to arrive. */
@media (hover:none){
  .ctile__veil{ opacity:.92; }
  .ctile__name{ opacity:1; transform:none; }
  .ctile__n{ opacity:1; transform:none; }
}

/* The count, as a floating badge and nothing else — the number, never the
   word, and only under the pointer. It sits ON the picture, whose ground is
   white in both themes, so it is painted against the artwork rather than the
   page: a dark translucent pill reads the same in light and dark, where a
   surface token would have gone white-on-white in one of them. */
.ctile__n{
  position:absolute; top:2px; right:2px; z-index:2; pointer-events:none;
  min-width:20px; height:18px; padding:0 5px; border-radius:99px;
  display:grid; place-items:center;
  font-size:10px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums;
  background:rgba(9,20,18,.74); color:#FFFFFF; border:1px solid rgba(255,255,255,.2);
  opacity:0; transform:translateY(-3px);
  transition:opacity .2s ease, transform .2s ease;
}
.ctile.is-empty .ctile__n{ background:rgba(9,20,18,.42); }

/* A category whose groups are still being collected. The badge says "Soon"
   where the count goes, and unlike the count it is ALWAYS on: a number is
   something you look up while choosing, but "this one has nothing in it yet"
   has to be readable before the tap, not after it. Same pill, same place,
   same size — only the word inside it differs. */
.ctile__n--soon{
  opacity:1; transform:none; letter-spacing:.02em; text-transform:uppercase;
  font-size:9px; background:var(--c);
}
.ctile:hover .ctile__n--soon,
.ctile:focus-visible .ctile__n--soon{ opacity:1; transform:none; }
/* Selected: the category's own colour, drawn as the card's edge and nothing
   more. The inset ring is the same border doubled, not a glow — it thickens
   the outline to 2px without softening it or tinting the white the photograph
   needs. There is no tick: on a grid where exactly one card can be outlined in
   colour, a badge saying so is a second answer to a question already
   answered, and it lands on top of the picture. */
.ctile.is-on{
  border-color:var(--c); box-shadow:0 0 0 1px var(--c) inset;
}

/* The "+" slot: the same cell, plainly empty. A dashed edge says "nothing here
   yet" without borrowing the weight of a real category. */
.ctile--add{ background:transparent; border-style:dashed; color:var(--ink-3); }
.ctile--add:hover{ border-color:var(--teal-600); color:var(--teal-700); background:var(--teal-050); }
.ctile--add .ctile__art{
  display:grid; place-items:center; gap:3px; align-content:center;
}
.ctiles--2 .ctile__plus svg{ width:24px; height:24px; }
.ctiles--2 .ctile__name--add{ font-size:11.5px; }

/* --- grid density control, in the panel head --------------------------- */
/* Small enough to sit beside the heading rather than becoming a settings
   panel: the two icons ARE the layouts they choose. */
.cols{
  display:inline-flex; padding:2px; gap:2px; border-radius:8px;
  background:var(--surface-3); border:1px solid var(--line);
}
.cols__opt{
  display:grid; place-items:center; width:22px; height:18px; border-radius:5px;
  color:var(--ink-3); transition:background .14s, color .14s;
}
.cols__opt svg{
  width:13px; height:13px;
  /* the icon is a pair/trio of filled bars, not an outline */
  fill:currentColor; stroke:none;
}
.cols__opt:hover{ color:var(--ink); }
.cols__opt.is-on{ background:var(--surface); color:var(--teal-700); box-shadow:var(--sh-1); }
.ctile__plus{ display:grid; place-items:center; }
.ctile__plus svg{ width:20px; height:20px; }
.ctile__name--add{
  position:static; opacity:1; transform:none; text-align:center;
  color:currentColor; text-shadow:none; font-size:10px; font-weight:700;
}

/* ---------------- RIGHT: brand explorer ---------------------------------- */
.brandsearch{ margin-bottom:9px; }
.brandsearch .input{ height:34px; font-size:13px; padding-left:33px; padding-right:30px; }
.brandsearch .field svg{ left:10px; width:15px; height:15px; }
.field__clear{
  position:absolute; right:6px; width:22px; height:22px; border-radius:99px;
  display:grid; place-items:center; background:var(--surface-3); color:var(--ink-3);
}
.field__clear:hover{ background:var(--teal-050); color:var(--teal-700); }
.field__clear svg{ position:static; width:12px; height:12px; pointer-events:none; }
.brandempty{
  display:flex; flex-direction:column; align-items:center; gap:8px; text-align:center;
  padding:22px 10px; color:var(--ink-3); font-size:12.5px; font-weight:600;
}
.brandempty svg{ width:22px; height:22px; opacity:.7; }
.brow--empty{ opacity:.52; }
.brow--empty:hover{ opacity:.85; }

.brows{ display:flex; flex-direction:column; gap:2px; }
/* The logo is the subject of this row, so the row is sized around it rather
   than around the text. It used to give the mark a 28px chip with 6px of
   padding — 16px of actual logo, which is why Motorola read as a speck and
   Huawei as unidentifiable grey text. */
.brow{
  display:flex; align-items:center; gap:10px; width:100%; padding:5px 7px;
  border-radius:var(--r-xs); text-align:left; border:1px solid transparent;
  transition:background .14s, border-color .14s, color .14s;
}
.brow:hover{ background:var(--surface-3); }
.brow__n{ font-size:13.5px; font-weight:700; letter-spacing:-.012em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.brow__c{ margin-left:auto; font-family:var(--f-mono); font-size:11px; color:var(--ink-3); flex:none; }
.brow__all{
  width:48px; height:40px; border-radius:10px; display:grid; place-items:center; flex:none;
  background:var(--surface-3); color:var(--ink-3);
}
.brow__all svg{ width:17px; height:17px; }
.brow.is-on{ background:var(--teal-050); border-color:var(--teal-100); }
.brow.is-on .brow__n,.brow.is-on .brow__c{ color:var(--teal-700); }
.brow.is-on .brow__all{ background:var(--grad-brand); color:#fff; }

/* ---------------- filter controls ---------------- */
.field{ position:relative; display:flex; align-items:center; }
.field svg{ position:absolute; left:11px; width:17px; height:17px; color:var(--ink-3); pointer-events:none; }
.input{
  width:100%; height:42px; border-radius:var(--r-sm); border:1px solid var(--line);
  background:var(--surface-2); padding:0 12px; font-size:14.5px; font-weight:600; outline:none;
  transition:border-color .16s, box-shadow .16s, background .16s;
}
.input::placeholder{ color:var(--ink-3); font-weight:500; }
.input:focus{ border-color:var(--teal-500); background:var(--surface); box-shadow:0 0 0 3px rgba(20,179,154,.16); }
.field .input{ padding-left:36px; }
select.input{ appearance:none; cursor:pointer; padding-right:34px;
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 17px) 19px,calc(100% - 12px) 19px; background-size:5px 5px,5px 5px; background-repeat:no-repeat;
}
.checkline{ display:flex; align-items:center; gap:10px; padding:7px 0; font-size:14px; font-weight:600; cursor:pointer; }
.checkline input{ accent-color:var(--teal-600); width:17px; height:17px; }

/* ---- horizontal product-category rail (under the main search) ----------- */
.crail{
  display:flex; gap:8px; margin-top:16px; padding:2px 0 4px;
  overflow-x:auto; -webkit-overflow-scrolling:touch;
  scroll-snap-type:x proximity; scrollbar-width:none;
}
.crail::-webkit-scrollbar{ display:none; }
/* The phone's category card. Same idea as the desktop tile — the picture IS
   the card — at the size a thumb can hit.

   A WHITE surface, deliberately, on the green band: these are product
   photographs shot on white, and a white card is the display surface they
   were lit for. Flat white with one hairline and one soft lift — no tint, no
   glass, no inner shading, because anything laid over the white is what makes
   it read as grey when you zoom in. */
.crail__item{
  --c:var(--teal-500);
  /* How much of the card's height the part fills. 0.80 leaves a tenth clear
     top and bottom, which is the breathing room a product shot needs to look
     placed rather than crammed. One number, and the only one to change if the
     parts should sit larger or smaller. */
  --crail-fit:0.93;
  flex:none; width:86px; scroll-snap-align:start;
  display:block; position:relative;
  padding:4px; border-radius:var(--r-sm);
  background:var(--ctile-paper);
  border:1px solid rgba(255,255,255,.5);
  box-shadow:0 2px 10px -4px rgba(3,26,23,.35);
  color:#fff; text-align:left;
  transition:border-color .16s, transform .16s cubic-bezier(.2,.8,.3,1), box-shadow .16s;
}
.crail__item:active{ transform:scale(.97); }
.crail__item:focus-visible{ outline:2px solid #fff; outline-offset:2px; }
/* Selected is the category's own colour, drawn thicker than the resting
   hairline so it reads at a glance across a scrolling strip — and that is the
   whole of it. No tick, no glow, nothing printed over the part. */
.crail__item.is-on{
  border-color:var(--c); box-shadow:0 0 0 2px var(--c) inset;
}
.crail__art{
  position:relative; display:block; width:100%; aspect-ratio:3/4;
  border-radius:8px; overflow:hidden;
}
/* Plain white behind the part and nothing else. The shared .pthumb carries an
   inset bottom shadow that reads as a grey smear at this size. */
.crail__art .pthumb--rail{
  position:absolute; inset:0; width:100%; height:100%;
  border-radius:8px; border-color:transparent; padding:0;
  background:var(--ctile-paper); box-shadow:none;
}

/* Name: only while the card is under a finger or a focus ring. Same overlay
   and the same bottom fade as the desktop tile. */
/* Shallower and softer than the desktop tile's: there it fades into a
   photograph, here it fades into white, where the same gradient reads as a
   grey band clamped across the bottom of a clean card. Just enough to carry
   one line of type. */
.crail__veil{
  position:absolute; left:0; right:0; bottom:0; height:42%; pointer-events:none;
  background:linear-gradient(to top, rgba(5,14,13,.82) 0%, rgba(5,14,13,.42) 48%, rgba(5,14,13,0) 100%);
  opacity:0; transition:opacity .18s ease;
}
.crail__name{
  position:absolute; left:5px; right:5px; bottom:4px; pointer-events:none;
  font-size:10px; font-weight:700; line-height:1.15; letter-spacing:-.01em;
  color:#FFFFFF; text-shadow:0 1px 2px rgba(0,0,0,.55);
  opacity:0; transform:translateY(3px); transition:opacity .18s ease, transform .18s ease;
  overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
}
/* The name belongs to the SELECTED card — the one whose contents the centre
   column is showing, so naming it answers "what am I looking at". A press
   shows it too: :active is the touch equivalent of hover and holds for as long
   as the finger is down, which is exactly when someone is asking. */
.crail__item.is-on .crail__veil,
.crail__item:active .crail__veil,
.crail__item:focus-visible .crail__veil{ opacity:1; }
.crail__item.is-on .crail__name,
.crail__item:active .crail__name,
.crail__item:focus-visible .crail__name{ opacity:1; transform:none; }

/* The count, on the SELECTED card only, in that category's own colour — so the
   ring and the number say the same thing rather than two different ones.

   It sits ABOVE the picture's top edge, over the card's own padding, not on
   the product: the parts are centred with a tenth of the card clear top and
   bottom, and a pill dropped inside that would land on the glass of a screen
   guard or the camera of a back cover. Its own white outline keeps it legible
   whatever the accent colour is against the white beneath. */
.crail__n{
  position:absolute; top:-1px; right:-1px; z-index:2; pointer-events:none;
  min-width:20px; height:18px; padding:0 5px; border-radius:99px;
  display:grid; place-items:center;
  font-size:9.5px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums;
  background:var(--c); color:#FFFFFF;
  border:1.5px solid var(--ctile-paper);
  box-shadow:0 1px 4px -1px rgba(3,26,23,.45);
  opacity:0; transform:translateY(-3px) scale(.9);
  transition:opacity .18s ease, transform .18s ease;
}
.crail__item.is-on .crail__n{ opacity:1; transform:none; }
/* The rail's version of the same badge. The count shows on the selected card
   only; "Soon" shows always, because a card that cannot be selected has no
   selected state to reveal it in. */
.crail__n--soon{
  opacity:1; transform:none; letter-spacing:.02em; text-transform:uppercase;
  font-size:8.5px;
}
.pthumb--rail{ width:46px; height:46px; }

/* group filter — a plain row inside the Compatibility Groups section */
.fbar{
  display:flex; gap:8px; align-items:center; margin:0 0 14px;
}

/* ---------------- RESULT PAGE ------------------------------------------- */
/* the app header is not repeated above the result head on small screens */
@media (max-width:1179px){
  .app.is-result .topbar{ display:none; }
}
.rhead{
  position:sticky; top:0; z-index:58;
  border-radius:0; padding:10px 0 11px;
  box-shadow:0 10px 24px -18px rgba(0,0,0,.8);
}
.rhead__row{ display:flex; align-items:center; gap:10px; }
.rhead__row .searchwrap{ flex:1 1 auto; min-width:0; margin-top:0; }
.rhead__row .search{ height:48px; }
.rhead__x{
  width:48px; height:48px; flex:none; border-radius:var(--r-sm);
  display:grid; place-items:center; color:#fff;
  background:rgba(255,255,255,.14); border:1px solid rgba(255,255,255,.22);
  transition:background .16s, transform .16s;
}
.rhead__x:hover{ background:rgba(255,255,255,.26); }
.rhead__x:active{ transform:scale(.95); }
.rhead__x svg{ width:20px; height:20px; }
/* the result rail is a touch tighter than the home one — this block is
   sticky, so it has to stay light on a phone screen */
.rhead .crail{ margin-top:10px; }
.rhead .crail__item{ width:68px; padding:7px 5px 6px; gap:5px; }
.rhead .pthumb--rail{ width:38px; height:38px; }
.rhead .crail__name{ font-size:10px; }
.crail__item.is-empty{ opacity:.42; pointer-events:none; }
@media (min-width:1180px){
  /* the app header already carries the search up here */
  .rhead{ position:static; }
  .rhead__row .searchwrap{ display:none; }
  .rhead__row{ justify-content:flex-end; }
}

/* category block heading inside a grouped "All Parts" result */
.catgroup{
  display:flex; align-items:center; gap:10px; margin:22px 0 12px;
  padding-bottom:9px; border-bottom:2px solid color-mix(in srgb,var(--c) 35%,var(--line));
}
.catgroup:first-child{ margin-top:4px; }
.pthumb--head{ width:32px; height:32px; border-radius:9px; }
.catgroup__name{
  font-family:var(--f-display); font-size:15px; font-weight:700; letter-spacing:-.015em;
  color:color-mix(in srgb,var(--c) 62%,var(--ink)); text-transform:uppercase;
}
.catgroup__n{
  margin-left:auto; font-family:var(--f-mono); font-size:11.5px; color:var(--ink-3);
  background:var(--surface-3); padding:3px 8px; border-radius:var(--r-xs);
}
@media (min-width:1180px){ .fbar{ display:none; } }
.fbar .field{ flex:1; min-width:0; }
.fbar__btn{ position:relative; }
.fbar__btn .dotn{
  position:absolute; top:-4px; right:-4px; min-width:18px; height:18px; padding:0 5px;
  border-radius:99px; background:var(--amber-500); color:#2A1405;
  font-size:10.5px; font-weight:800; display:grid; place-items:center;
}

/* ---------------- category chips / grid ---------------- */
.cats{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
@media (min-width:560px){ .cats{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
@media (min-width:900px){ .cats{ grid-template-columns:repeat(4,minmax(0,1fr)); } }
.cat{
  position:relative; overflow:hidden; text-align:left; padding:13px 13px 12px;
  border-radius:var(--r); border:1px solid var(--line); background:var(--surface);
  box-shadow:var(--sh-1); transition:transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .18s, border-color .18s;
}
.cat:hover{ transform:translateY(-2px); box-shadow:var(--sh-2); }
.cat::after{ content:""; position:absolute; left:0; top:0; bottom:0; width:3px; background:var(--c); }
.pthumb--cat{ width:34px; height:34px; margin-bottom:9px; }
.cat__name{ font-weight:700; font-size:14px; letter-spacing:-.015em; line-height:1.25; }
.cat__n{ font-size:11.5px; color:var(--ink-3); font-weight:600; margin-top:3px; font-variant-numeric:tabular-nums; }
.cat.is-on{ border-color:var(--c); box-shadow:0 0 0 2px color-mix(in srgb,var(--c) 26%,transparent), var(--sh-2); }
.cat.is-off{ opacity:.42; }
.cat__tick{ position:absolute; top:10px; right:10px; color:var(--c); opacity:0; transition:opacity .16s; }
.cat__tick svg{ width:17px; height:17px; }
.cat.is-on .cat__tick{ opacity:1; }


/* ---------------- part-label group card ---------------- */
/* ---- the two-part group feed -------------------------------------------
   A trending strip over the catalogue. The strip is a single row that scrolls
   SIDEWAYS: stacked, twelve recent groups would push the catalogue itself off
   the first screen to say something the catalogue already says. */
.feed + .feed{ margin-top:22px; }
.feed__head{
  display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; margin:0 0 11px;
}
.feed__title{
  display:flex; align-items:center; gap:7px; margin:0;
  font-family:var(--f-display); font-size:15px; font-weight:800; letter-spacing:-.015em;
}
.feed__title svg{ width:15px; height:15px; color:var(--amber-600); }
.feed__note{ font-size:11.5px; font-weight:600; color:var(--ink-3); }
.feed__head--rest{
  padding-top:16px; border-top:1px solid var(--line-soft);
}

/* Cards keep their real width and run off the edge, which is what tells the
   reader there is more to the right. Only this element scrolls — the page
   never gains a horizontal scrollbar, because the row is the overflow
   container and min-width:0 lets it be narrower than its contents. */
.trendrow,.suggestrow,.modelrow{
  display:grid; grid-auto-flow:column; grid-auto-columns:minmax(258px,262px);
  gap:14px; min-width:0;
  overflow-x:auto; overflow-y:hidden; scroll-snap-type:x proximity;
  scroll-padding-left:2px; -webkit-overflow-scrolling:touch;
  padding-bottom:10px;                   /* room for the scrollbar, not the cards */
  scrollbar-width:thin; scrollbar-color:var(--line) transparent;
}
/* Two rows deep, filled COLUMN BY COLUMN — grid-auto-flow:column with two
   template rows puts rank 1 and 2 in the first column, 3 and 4 in the second.
   So the strongest groups are the ones already on screen, and scrolling right
   walks down the ranking rather than across a row of it. */
.suggestrow{ grid-template-rows:repeat(2,auto); align-items:start; }

/* ---- new-model card -----------------------------------------------------
   A product card: the manufacturer's own press shot on top, the name and size
   under it, one action. Narrower columns than the group plates, because a
   phone photograph is portrait and a wide card would be mostly margin. */
.modelrow{ grid-auto-columns:minmax(158px,172px); }
.ncard{
  position:relative; display:flex; flex-direction:column; height:100%;
  border-radius:var(--r); overflow:hidden;
  background:var(--surface); border:1px solid var(--line);
  transition:transform .18s cubic-bezier(.2,.8,.3,1), border-color .18s, box-shadow .18s;
}
.ncard:hover{ transform:translateY(-2px); border-color:var(--teal-500); box-shadow:var(--sh-1); }
/* The press shots are photographs on white, so the well is white in both
   themes — the same reasoning as the category tiles: matching the picture's
   own ground is what stops it reading as a pasted-on rectangle. */
.ncard__shot{
  position:relative; display:grid; place-items:center;
  aspect-ratio:1/1; background:var(--ctile-paper); padding:10px;
  border-bottom:1px solid var(--line);
}
/* A DEFINITE box, not max-width on an auto size. An image sized only by its
   own intrinsic dimensions is 0x0 until it loads, a zero-area box never
   intersects the viewport, and a lazy image that never intersects never loads
   — so it stays 0x0 for good. Filling the well instead gives it area from the
   first frame, which is what lets loading="lazy" actually fire, and contain
   keeps every shot at its true proportions inside one consistent area. */
.ncard__img,.ncard__svg{
  width:100%; height:100%; object-fit:contain; display:block;
}
.ncard__body{ padding:9px 10px 34px; display:flex; flex-direction:column; gap:3px; }
.ncard__n{
  margin:0; font-size:12.5px; font-weight:700; line-height:1.25; letter-spacing:-.012em;
  color:var(--ink);
  overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
}
.ncard__m{
  font-size:11px; font-weight:600; color:var(--ink-3);
  display:flex; align-items:center; gap:6px;
}
.ncard__m i{ font-style:normal; width:3px; height:3px; border-radius:99px; background:currentColor; }
/* The only action, and an icon alone — its accessible name carries the model,
   so nothing is lost by not printing "Find parts" on every card. */
.ncard__go{
  position:absolute; right:8px; bottom:8px;
  width:30px; height:30px; border-radius:99px; display:grid; place-items:center;
  background:var(--teal-050); color:var(--teal-700); border:1px solid var(--teal-100);
  transition:background .16s, color .16s, transform .16s;
}
.ncard__go svg{ width:15px; height:15px; }
.ncard__go:hover{ background:var(--grad-brand); color:#fff; border-color:transparent; transform:scale(1.06); }
.ncard__go:focus-visible{ outline:2px solid var(--teal-600); outline-offset:2px; }

/* "All models" beside the section heading — a way out to the full catalogue
   from a strip that is deliberately only fourteen of it. */
.feed__more{
  margin-left:auto; display:inline-flex; align-items:center; gap:4px;
  font-size:12px; font-weight:700; color:var(--teal-700);
  padding:3px 7px; border-radius:var(--r-xs);
  transition:background .14s;
}
.feed__more:hover{ background:var(--teal-050); }
.feed__more svg{ width:14px; height:14px; }

.trendrow > *,.suggestrow > *,.modelrow > *{ scroll-snap-align:start; }
.trendrow::-webkit-scrollbar,
.suggestrow::-webkit-scrollbar,
.modelrow::-webkit-scrollbar{ height:8px; }
.trendrow::-webkit-scrollbar-track,
.suggestrow::-webkit-scrollbar-track,
.modelrow::-webkit-scrollbar-track{ background:transparent; }
.trendrow::-webkit-scrollbar-thumb,
.suggestrow::-webkit-scrollbar-thumb,
.modelrow::-webkit-scrollbar-thumb{
  background:var(--line); border-radius:99px; border:2px solid transparent; background-clip:content-box;
}
.trendrow:hover::-webkit-scrollbar-thumb,
.suggestrow:hover::-webkit-scrollbar-thumb,
.modelrow:hover::-webkit-scrollbar-thumb{ background:var(--ink-3); background-clip:content-box; }
@media (max-width:520px){
  .trendrow,.suggestrow{ grid-auto-columns:minmax(232px,246px); }
  .modelrow{ grid-auto-columns:minmax(216px,240px); }
}

/* ---- carousel arrows ----------------------------------------------------
   Outside the cards, not over them: an arrow floating on a plate hides the
   part code it is sitting on. They sit in the gutter the centre column
   already has, appear only where there is somewhere to go, and are hidden
   entirely on touch, where the gesture is the control. */
.rail{ position:relative; }
.rail__nav{
  position:absolute; top:calc(50% - 5px); transform:translateY(-50%); z-index:3;
  width:30px; height:30px; border-radius:99px; display:grid; place-items:center;
  background:var(--surface); border:1px solid var(--line); color:var(--ink-2);
  box-shadow:var(--sh-2);
  transition:opacity .16s, background .16s, color .16s, visibility .16s;
}
.rail__nav svg{ width:16px; height:16px; }
.rail__nav:hover{ background:var(--teal-050); color:var(--teal-700); border-color:var(--teal-100); }
.rail__nav--prev{ left:-13px; }
.rail__nav--next{ right:-13px; }
.rail__nav:disabled{ opacity:0; visibility:hidden; pointer-events:none; }
.rail.is-static .rail__nav{ opacity:0; visibility:hidden; pointer-events:none; }
@media (hover:none){ .rail__nav{ display:none; } }

.gridcards{ display:grid; gap:14px; grid-template-columns:1fr; }
@media (min-width:640px){ .gridcards{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (min-width:1060px){ .gridcards{ grid-template-columns:repeat(3,minmax(0,1fr)); } }

.plate{
  --c:var(--teal-600);
  position:relative; display:flex; flex-direction:column; text-align:left; width:100%;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);
  box-shadow:var(--sh-1); overflow:hidden;
  cursor:pointer;
  transition:transform .2s cubic-bezier(.2,.8,.3,1), box-shadow .2s, border-color .2s;
}
.plate:hover{ transform:translateY(-3px); box-shadow:var(--sh-2); border-color:color-mix(in srgb,var(--c) 40%,var(--line)); }
/* printed label strip */
.plate__strip{
  display:flex; align-items:center; gap:8px; padding:8px 12px;
  background:color-mix(in srgb,var(--c) 9%,var(--surface-2));
  border-bottom:1px dashed color-mix(in srgb,var(--c) 32%,var(--line));
  font-family:var(--f-mono); font-size:11.5px; font-weight:600; color:color-mix(in srgb,var(--c) 72%,var(--ink));
  min-width:0;
}
.plate__strip > span:not(.plate__catbadge){ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.plate__strip .sep{ opacity:.4; flex:none; }
.plate__catbadge{ flex:none; }
.plate__strip .cat{ margin-left:auto; }
.plate__catbadge{
  margin-left:auto; display:inline-flex; align-items:center; gap:5px; height:21px; padding:0 8px;
  border-radius:var(--r-pill); background:var(--c); color:#fff;
  font-family:var(--f-ui); font-size:10.5px; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
}
.plate__body{ padding:14px; display:flex; flex-direction:column; gap:12px; flex:1; }
.plate__master{ display:flex; gap:12px; align-items:center; }
.plate__mname{ font-family:var(--f-display); font-size:18.5px; font-weight:700; letter-spacing:-.028em; line-height:1.18; }
@media (min-width:768px){ .plate__mname{ font-size:19.5px; } }
.plate__mmeta{ display:flex; align-items:center; gap:7px; margin-top:4px; font-size:12px; color:var(--ink-3); font-weight:600; }
.plate__mmeta i{ font-style:normal; width:3px; height:3px; border-radius:99px; background:currentColor; }
.masterflag{
  display:inline-flex; align-items:center; gap:5px; height:20px; padding:0 7px; border-radius:5px;
  background:var(--amber-050); border:1px solid var(--amber-100); color:var(--amber-700);
  font-size:10px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; margin-bottom:5px;
}
.masterflag svg{ width:11px; height:11px; }

.fitline{
  display:flex; align-items:center; gap:9px; padding:9px 11px; border-radius:var(--r-sm);
  background:var(--surface-2); border:1px solid var(--line-soft);
}
.fitline__n{ font-family:var(--f-display); font-size:22px; font-weight:800; letter-spacing:-.04em; color:var(--c); line-height:1; font-variant-numeric:tabular-nums; }
.fitline__t{ font-size:12px; font-weight:700; color:var(--ink-2); line-height:1.25; }
.fitline__bar{ margin-left:auto; display:flex; gap:3px; align-items:flex-end; height:22px; }
.fitline__bar span{ width:4px; border-radius:2px; background:color-mix(in srgb,var(--c) 45%,var(--line)); }

.devchips{ display:flex; flex-wrap:wrap; gap:6px; }
.devchip{
  font-size:12px; font-weight:600; padding:4px 9px; border-radius:var(--r-pill);
  background:var(--surface-3); color:var(--ink-2); border:1px solid transparent;
  max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.devchip--more{ background:transparent; border-color:var(--line); color:var(--ink-3); font-weight:700; }
.devchip--hit{ background:var(--teal-050); border-color:var(--teal-100); color:var(--teal-700); font-weight:700; }
.plate__foot{
  margin-top:auto; display:flex; align-items:center; gap:10px; padding:11px 14px;
  border-top:1px solid var(--line-soft); background:var(--surface-2);
}
.plate__foot .mono{ font-size:11px; color:var(--ink-3); }
.plate__cta{ margin-left:auto; display:inline-flex; align-items:center; gap:6px; font-size:13px; font-weight:700; color:var(--c); }
.plate__cta svg{ width:15px; height:15px; transition:transform .18s; }
.plate:hover .plate__cta svg{ transform:translateX(3px); }

.idgrid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:1px; background:var(--line-soft); border:1px solid var(--line); border-radius:var(--r); overflow:hidden; }
@media (min-width:560px){ .idgrid{ grid-template-columns:repeat(4,minmax(0,1fr)); } }
.idcell{ background:var(--surface); padding:10px 12px; min-width:0; }
.idcell span{ display:block; font-size:10px; font-weight:800; letter-spacing:.1em; text-transform:uppercase; color:var(--ink-3); }
.idcell b{ display:block; font-family:var(--f-mono); font-size:13px; font-weight:600; margin-top:3px; letter-spacing:-.02em; overflow:hidden; text-overflow:ellipsis; }

/* ---------------- compatible device list ---------------- */
.complist__head{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:10px; }
.devlist{ display:flex; flex-direction:column; gap:1px; background:var(--line-soft); border:1px solid var(--line); border-radius:var(--r); overflow:hidden; }
.dev{
  display:flex; align-items:center; gap:10px; padding:9px 12px; background:var(--surface); text-align:left; width:100%;
  transition:background .14s;
}
.dev:hover{ background:var(--surface-2); }
.dev__i{ font-family:var(--f-mono); font-size:11px; color:var(--ink-3); width:26px; flex:none; }
.dev__n{ font-weight:600; font-size:14px; letter-spacing:-.012em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dev__b{ margin-left:auto; font-size:11.5px; font-weight:700; color:var(--ink-3); flex:none; }
.dev.is-master{ background:var(--amber-050); }
.dev.is-hit{ background:var(--teal-050); box-shadow:inset 3px 0 0 var(--teal-500); }
.dev.is-hit .dev__n{ font-weight:800; color:var(--teal-700); }

.expandbtn{
  width:100%; height:44px; border-radius:var(--r-sm); border:1px dashed var(--line);
  color:var(--ink-2); font-weight:700; font-size:13.5px; display:inline-flex; align-items:center;
  justify-content:center; gap:8px; background:var(--surface-2); transition:all .16s;
}
.expandbtn:hover{ border-color:var(--teal-500); color:var(--teal-700); background:var(--teal-050); }
.expandbtn svg{ width:16px; height:16px; transition:transform .2s; }
.expandbtn.is-open svg{ transform:rotate(180deg); }

/* ---------------- model cards / brands ---------------- */
.brandgrid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:12px; }
@media (min-width:600px){ .brandgrid{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
@media (min-width:960px){ .brandgrid{ grid-template-columns:repeat(4,minmax(0,1fr)); } }
@media (min-width:1240px){ .brandgrid{ grid-template-columns:repeat(5,minmax(0,1fr)); } }
.brandcard{
  position:relative; overflow:hidden; text-align:left; padding:16px; border-radius:var(--r-lg);
  background:var(--surface); border:1px solid var(--line); box-shadow:var(--sh-1);
  transition:transform .2s cubic-bezier(.2,.8,.3,1), box-shadow .2s, border-color .2s;
}
.brandcard:hover{ transform:translateY(-3px); box-shadow:var(--sh-2); }
.brandcard::after{
  content:""; position:absolute; right:-40px; top:-40px; width:120px; height:120px; border-radius:99px;
  background:radial-gradient(circle,color-mix(in srgb,var(--b1) 26%,transparent),transparent 68%);
}
.brandcard__n{ font-family:var(--f-display); font-size:17px; font-weight:700; letter-spacing:-.025em; margin-top:11px; }
.brandcard__c{ font-size:12px; color:var(--ink-3); font-weight:600; font-variant-numeric:tabular-nums; }

.modelgrid{ display:grid; grid-template-columns:1fr; gap:10px; }
@media (min-width:640px){ .modelgrid{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (min-width:1100px){ .modelgrid{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
.mcard{
  display:flex; align-items:center; gap:12px; text-align:left; width:100%; padding:12px 14px;
  border-radius:var(--r); background:var(--surface); border:1px solid var(--line); box-shadow:var(--sh-1);
  content-visibility:auto; contain-intrinsic-size:auto 74px;
  transition:transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .18s, border-color .18s;
}
.mcard:hover{ transform:translateY(-2px); box-shadow:var(--sh-2); border-color:var(--teal-500); }
.mcard__n{ font-weight:700; font-size:15px; letter-spacing:-.018em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mcard__m{ font-size:12px; color:var(--ink-3); font-weight:600; display:flex; gap:7px; align-items:center; }
.mcard__m i{ font-style:normal; width:3px; height:3px; border-radius:99px; background:currentColor; }
.mcard__go{ margin-left:auto; color:var(--ink-3); flex:none; }
.mcard__go svg{ width:17px; height:17px; }

/* spec sheet */
.specs{ display:grid; grid-template-columns:1fr; gap:1px; background:var(--line-soft); border:1px solid var(--line); border-radius:var(--r); overflow:hidden; }
@media (min-width:520px){ .specs{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
.spec{ background:var(--surface); padding:11px 13px; display:flex; align-items:baseline; justify-content:space-between; gap:12px; }
.spec dt{ font-size:12.5px; color:var(--ink-3); font-weight:600; }
.spec dd{ margin:0; font-size:13.5px; font-weight:700; text-align:right; font-variant-numeric:tabular-nums; }

/* ---------------- overlay: sheet + modal ---------------- */
.scrim{
  position:fixed; inset:0; z-index:90; background:rgba(6,20,18,.5);
  backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px);
  animation:fade .2s ease;
}
@keyframes fade{ from{ opacity:0; } }
.sheet{
  position:fixed; z-index:100; left:0; right:0; bottom:0; max-height:92dvh;
  background:var(--surface); border-radius:var(--r-xl) var(--r-xl) 0 0;
  box-shadow:var(--sh-3); display:flex; flex-direction:column; overflow:hidden;
  animation:slideup .3s cubic-bezier(.2,.9,.25,1);
}
@keyframes slideup{ from{ transform:translateY(6%); opacity:.6; } }
@media (min-width:820px){
  .sheet{
    left:50%; right:auto; bottom:auto; top:50%; transform:translate(-50%,-50%);
    width:min(860px,calc(100vw - 64px)); max-height:min(88dvh,880px); border-radius:var(--r-xl);
    animation:zoomin .22s cubic-bezier(.2,.9,.25,1);
  }
  @keyframes zoomin{ from{ transform:translate(-50%,-48%) scale(.97); opacity:0; } }
}
.sheet__grab{ width:38px; height:4px; border-radius:99px; background:var(--line); margin:9px auto 0; flex:none; }
@media (min-width:820px){ .sheet__grab{ display:none; } }
.sheet__head{
  display:flex; align-items:center; gap:12px; padding:12px 16px; border-bottom:1px solid var(--line-soft); flex:none;
}
.sheet__body{ overflow-y:auto; padding:16px; display:flex; flex-direction:column; gap:16px; overscroll-behavior:contain; }
@media (min-width:820px){ .sheet__body{ padding:20px; } }
.sheet__foot{ flex:none; padding:12px 16px calc(12px + env(safe-area-inset-bottom)); border-top:1px solid var(--line-soft); background:var(--surface-2); display:flex; gap:10px; }

/* ---------------- states ---------------- */
.state{ text-align:center; padding:38px 20px; display:flex; flex-direction:column; align-items:center; gap:12px; }
.state__art{ width:78px; height:78px; border-radius:22px; display:grid; place-items:center; background:var(--surface-3); color:var(--ink-3); }
.state__art svg{ width:34px; height:34px; }
.state__art--brand{ background:var(--teal-050); color:var(--teal-600); }
.state h3{ font-size:18px; }
.state p{ color:var(--ink-2); font-size:14px; max-width:44ch; }

.skel{ background:linear-gradient(90deg,var(--surface-3) 25%,var(--surface-2) 37%,var(--surface-3) 63%); background-size:400% 100%; animation:sh 1.3s ease-in-out infinite; border-radius:8px; }
@keyframes sh{ 0%{ background-position:100% 50%; } 100%{ background-position:0 50%; } }
.skelplate{ border-radius:var(--r-lg); border:1px solid var(--line); background:var(--surface); padding:14px; display:flex; flex-direction:column; gap:11px; }

/* ---------------- paywall ---------------- */
.paywall{
  position:relative; overflow:hidden; border-radius:var(--r-xl); padding:22px 18px;
  background:linear-gradient(150deg,#2A1405 0%,#7A3B0C 55%,#B45309 100%); color:#FFF3E6;
  box-shadow:var(--sh-2); text-align:center;
}
.paywall::before{
  content:""; position:absolute; inset:0; opacity:.35;
  background-image:linear-gradient(rgba(255,255,255,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.07) 1px,transparent 1px);
  background-size:26px 26px;
  mask-image:radial-gradient(90% 70% at 50% 0%,#000,transparent 75%);
  -webkit-mask-image:radial-gradient(90% 70% at 50% 0%,#000,transparent 75%);
}
.paywall > *{ position:relative; z-index:1; }
.paywall h3{ color:#fff; font-size:clamp(19px,3.4vw,25px); margin-top:12px; }
.paywall p{ color:rgba(255,240,225,.82); font-size:14px; margin:8px auto 0; max-width:46ch; }
.paywall__lock{ width:52px; height:52px; border-radius:16px; margin:0 auto; display:grid; place-items:center; background:rgba(255,255,255,.14); border:1px solid rgba(255,255,255,.2); color:#FFD9A8; }
.paywall__lock svg{ width:24px; height:24px; }
.paywall__acts{ display:flex; flex-wrap:wrap; gap:10px; justify-content:center; margin-top:16px; }

/* deliberate locked state — replaces the old blurred-data treatment.
   Nothing real is ever obscured; restricted fields are simply not rendered. */
.lockbox{
  display:flex; align-items:center; gap:12px; padding:13px 14px;
  border-radius:var(--r); border:1px dashed var(--amber-100);
  background:var(--amber-050);
}
.lockbox__ico{
  width:34px; height:34px; flex:none; border-radius:10px; display:grid; place-items:center;
  background:var(--amber-100); color:var(--amber-700);
}
.lockbox__ico svg{ width:18px; height:18px; }
.lockbox__body{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.lockbox__body b{ font-size:13.5px; font-weight:700; color:var(--ink); letter-spacing:-.01em; }
.lockbox__body span{ font-size:12px; color:var(--ink-2); }

/* ---------------- plans ---------------- */
.plans{ display:grid; gap:16px; grid-template-columns:1fr; }
@media (min-width:820px){ .plans{ grid-template-columns:repeat(2,minmax(0,1fr)); align-items:start; } }
.plan{
  position:relative; overflow:hidden; border-radius:var(--r-xl); border:1px solid var(--line);
  background:var(--surface); box-shadow:var(--sh-1); padding:20px;
}
.plan--hero{ border-color:transparent; background:var(--grad-bench); color:#EAFBF6; box-shadow:var(--sh-3); }
.plan--hero::after{ content:""; position:absolute; right:-90px; bottom:-90px; width:280px; height:280px; border-radius:99px; background:radial-gradient(circle,rgba(255,138,61,.3),transparent 66%); }
.plan__in{ position:relative; z-index:1; }
.plan__tag{
  position:absolute; top:16px; right:16px; z-index:2; height:26px; padding:0 11px; border-radius:var(--r-pill);
  background:var(--grad-amber); color:#2A1405; font-size:11px; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; display:inline-flex; align-items:center;
}
.plan__name{ font-family:var(--f-display); font-size:19px; font-weight:700; letter-spacing:-.025em; }
.plan--hero .plan__name{ color:#fff; }
.plan__price{ display:flex; align-items:baseline; gap:6px; margin-top:10px; }
.plan__price b{ font-family:var(--f-display); font-size:clamp(38px,8vw,52px); font-weight:800; letter-spacing:-.045em; line-height:1; }
.plan__price span{ font-size:14px; font-weight:700; color:var(--ink-3); }
.plan--hero .plan__price span{ color:rgba(214,255,244,.7); }
.plan__note{ font-size:13px; margin-top:8px; color:var(--ink-2); }
.plan--hero .plan__note{ color:rgba(214,255,244,.78); }
.plan__feats{ display:flex; flex-direction:column; gap:9px; margin:16px 0 18px; }
.plan__feats li{ display:flex; gap:9px; align-items:flex-start; font-size:14px; font-weight:600; }
.plan__feats svg{ width:17px; height:17px; flex:none; margin-top:2px; color:var(--teal-600); }
.plan--hero .plan__feats svg{ color:var(--amber-400); }

/* ---------------- auth: Google button, registration form, country picker -- */
.gbtn{
  display:flex; align-items:center; justify-content:center; gap:11px; width:100%;
  height:52px; padding:0 18px; border-radius:var(--r-sm);
  background:var(--surface); color:var(--ink); border:1px solid var(--line);
  font-size:15.5px; font-weight:700; letter-spacing:-.01em; box-shadow:var(--sh-1);
  transition:border-color .16s, box-shadow .16s, transform .16s, opacity .16s;
}
.gbtn:hover{ border-color:var(--ink-3); box-shadow:var(--sh-2); }
.gbtn:active{ transform:translateY(1px); }
.gbtn__g{ display:grid; place-items:center; width:20px; height:20px; flex:none; }
.gbtn.is-disabled,.gbtn[disabled]{
  opacity:.5; cursor:not-allowed; box-shadow:none;
  background:var(--surface-2); color:var(--ink-3); border-style:dashed;
}
.gbtn.is-disabled:hover{ border-color:var(--line); box-shadow:none; }
.gbtn.is-busy{ opacity:.7; pointer-events:none; }

.regform{ display:flex; flex-direction:column; gap:12px; margin-top:18px; }
.regform__sub{
  display:flex; align-items:baseline; justify-content:space-between; gap:10px;
  margin-top:6px; padding-top:14px; border-top:1px solid var(--line-soft);
}
.ffield{ display:flex; flex-direction:column; gap:5px; min-width:0; }
.ffield .t-lab{ display:block; }
.ffield__err{ font-size:11.5px; font-weight:700; color:var(--bad); }
.ffield.has-error .input{ border-color:var(--bad); background:var(--bad-bg); }
.ffield.has-error .input:focus{ box-shadow:0 0 0 3px rgba(220,75,82,.18); }
.ffield--pair{ display:grid; grid-template-columns:1fr 1fr; gap:10px; }
@media (max-width:379px){ .ffield--pair{ grid-template-columns:1fr; } }

.phonerow{ display:flex; gap:8px; align-items:stretch; }
.phonerow .input{ flex:1 1 auto; min-width:0; }
.ccpick{
  display:flex; align-items:center; gap:6px; flex:none; max-width:46%;
  height:42px; padding:0 9px; border-radius:var(--r-sm);
  border:1px solid var(--line); background:var(--surface-2); color:var(--ink);
  font-size:13.5px; font-weight:600;
}
.ccpick:hover{ border-color:var(--ink-3); }
.ccpick__flag{ font-size:17px; line-height:1; }
.ccpick__name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ccpick__dial{ color:var(--ink-2); font-family:var(--f-mono); font-size:12.5px; }
.ccpick svg{ width:14px; height:14px; color:var(--ink-3); flex:none; }

.clist{ display:flex; flex-direction:column; gap:1px; background:var(--line-soft);
  border:1px solid var(--line); border-radius:var(--r); overflow:hidden; }
.crow{
  display:flex; align-items:center; gap:11px; padding:11px 13px; width:100%;
  background:var(--surface); text-align:left; transition:background .14s;
}
.crow:hover{ background:var(--surface-2); }
.crow.is-on{ background:var(--teal-050); }
.crow.is-on .crow__name{ color:var(--teal-700); font-weight:800; }
.crow__flag{ font-size:20px; line-height:1; flex:none; }
.crow__name{ font-size:14px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.crow__dial{ margin-left:auto; font-family:var(--f-mono); font-size:12.5px; color:var(--ink-3); flex:none; }

/* ---------------- account: access list, plans, meter, profile edit ------- */
.acclist{ display:flex; flex-direction:column; gap:9px; }
.acclist li{ display:flex; gap:10px; align-items:flex-start; font-size:14px; font-weight:600; }
.acclist svg{ width:17px; height:17px; flex:none; margin-top:2px; }
.acclist__on svg{ color:var(--ok); }
.acclist__off{ color:var(--ink-3); }
.acclist__off svg{ color:var(--ink-3); }
.acclist__tag{
  margin-left:auto; flex:none; font-size:10px; font-weight:800; letter-spacing:.06em;
  text-transform:uppercase; color:var(--amber-700); background:var(--amber-050);
  border:1px solid var(--amber-100); border-radius:var(--r-pill); padding:2px 8px;
}

.planpick{ display:grid; gap:12px; grid-template-columns:1fr; }
@media (min-width:560px){ .planpick{ grid-template-columns:1fr 1fr; } }
.planpick__item{
  position:relative; border:1px solid var(--line); border-radius:var(--r);
  background:var(--surface-2); padding:14px 13px 13px;
}
.planpick__item--best{
  border-color:var(--amber-100); background:var(--amber-050);
  box-shadow:0 0 0 1px var(--amber-100) inset;
}
.planpick__item.is-current{ border-color:var(--teal-500); background:var(--teal-050); }
.planpick__tag{
  position:absolute; top:-9px; right:12px; height:20px; padding:0 9px; border-radius:var(--r-pill);
  background:var(--grad-amber); color:#2A1405; font-size:10px; font-weight:800;
  letter-spacing:.05em; text-transform:uppercase; display:inline-flex; align-items:center;
}
.planpick__top{ display:flex; align-items:baseline; justify-content:space-between; gap:8px; }
.planpick__name{ font-family:var(--f-display); font-size:16px; font-weight:700; letter-spacing:-.02em; }
.planpick__price b{ font-family:var(--f-display); font-size:22px; font-weight:800; letter-spacing:-.04em; }
.planpick__price span{ font-size:11.5px; color:var(--ink-3); font-weight:700; }
.planpick__note{ font-size:12px; color:var(--ink-2); margin:6px 0 12px; }

.submeter{ margin-top:14px; }
.submeter__bar{ height:9px; border-radius:99px; background:var(--surface-3); overflow:hidden; }
.submeter__bar i{ display:block; height:100%; border-radius:99px; background:var(--grad-brand); transition:width .4s ease; }
.submeter__row{
  display:flex; justify-content:space-between; gap:10px; margin-top:7px;
  font-size:12.5px; font-weight:600; color:var(--ink-2);
}
.submeter__row b{ color:var(--ink); font-size:14px; }

.avatar--img{ padding:0; overflow:hidden; background:var(--surface-3); }
.avatar--img img{ width:100%; height:100%; object-fit:cover; display:block; }

.editform{ display:flex; flex-direction:column; gap:12px; }
.phrow{ display:flex; gap:14px; align-items:flex-start; }
.locbox{
  border:1px dashed var(--line); border-radius:var(--r);
  background:var(--surface-2); padding:12px;
}
.locpin{ display:flex; gap:10px; align-items:center; font-size:13.5px; }
.locpin svg{ width:18px; height:18px; flex:none; color:var(--ok); }
.locpin b{ display:block; font-size:13.5px; }
.locpin .mono{ font-size:11.5px; color:var(--ink-3); }

/* ---------------- account ---------------- */
/* minmax(0, …), not a bare fr. A bare 1fr track cannot shrink below its
   content's min-content width, so one unbreakable line inside a card — a long
   Gmail address, a nowrap button label — widened the column, the card and then
   the whole page past the phone's edge. */
.acct{ display:grid; gap:16px; grid-template-columns:minmax(0,1fr); }
@media (min-width:900px){ .acct{ grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr); align-items:start; } }
.statusbar{ height:8px; border-radius:99px; background:var(--surface-3); overflow:hidden; }
.statusbar i{ display:block; height:100%; border-radius:99px; background:var(--grad-brand); }
.segmented{ display:flex; gap:4px; padding:4px; background:var(--surface-3); border-radius:var(--r-sm); }
.segmented button{ flex:1; height:36px; border-radius:8px; font-size:13.5px; font-weight:700; color:var(--ink-2); transition:all .16s; }
.segmented button.is-on{ background:var(--surface); color:var(--ink); box-shadow:var(--sh-1); }

/* ---------------- toast ---------------- */
.toasts{ position:fixed; left:50%; transform:translateX(-50%); bottom:calc(var(--tabbar-h) + 16px + env(safe-area-inset-bottom)); z-index:200; display:flex; flex-direction:column; gap:8px; width:min(420px,calc(100vw - 32px)); pointer-events:none; }
@media (min-width:940px){ .toasts{ bottom:24px; } }
.toast{
  display:flex; align-items:center; gap:10px; padding:11px 14px; border-radius:var(--r-sm);
  background:var(--ink); color:var(--paper); box-shadow:var(--sh-3); font-size:13.5px; font-weight:600;
  animation:toastin .26s cubic-bezier(.2,.9,.25,1);
}
@keyframes toastin{ from{ opacity:0; transform:translateY(10px) scale(.97); } }
.toast svg{ width:17px; height:17px; flex:none; color:var(--teal-400); }

/* ---------------- misc ---------------- */
.sentinel{ height:1px; }
.loadmore{ display:flex; justify-content:center; padding:20px 0 4px; }
.copybtn{ display:inline-flex; align-items:center; gap:5px; font-family:var(--f-mono); font-size:12px; font-weight:600; color:var(--ink-2); padding:4px 8px; border-radius:7px; background:var(--surface-3); transition:all .14s; }
.copybtn:hover{ background:var(--teal-050); color:var(--teal-700); }
.copybtn svg{ width:13px; height:13px; }
.crumbs{ display:flex; align-items:center; gap:7px; font-size:13px; font-weight:600; color:var(--ink-3); flex-wrap:wrap; }
.crumbs button:hover{ color:var(--teal-700); }
.crumbs svg{ width:14px; height:14px; }
.notice{ display:flex; gap:11px; padding:12px 14px; border-radius:var(--r); border:1px solid var(--line); background:var(--surface-2); font-size:13.5px; }
.notice svg{ width:18px; height:18px; flex:none; color:var(--ink-3); margin-top:1px; }
.notice--amber{ background:var(--amber-050); border-color:var(--amber-100); }
.notice--amber svg{ color:var(--amber-600); }
.notice--brand{ background:var(--teal-050); border-color:var(--teal-100); }
.notice--brand svg{ color:var(--teal-600); }

/* ---------------------------------------------------------------- paywall
   Drawn wherever a compatibility list is withheld: an opened group, a match
   card, the device page. It is the same component in all three, because three
   wordings of one rule read as three rules.

   Deliberately NOT a blur or a fade over rows. There are no rows underneath
   it — the names never left the server — so anything that looked like
   obscured content would be a lie about what the page is holding. */
.paywall{
  display:flex; gap:14px; align-items:flex-start;
  padding:18px; border-radius:var(--r);
  border:1px solid var(--teal-100); background:var(--teal-050);
}
.paywall__ico{
  flex:none; display:grid; place-items:center;
  width:38px; height:38px; border-radius:50%;
  background:var(--teal-100); color:var(--teal-700);
}
.paywall__ico svg{ width:19px; height:19px; }
.paywall__b{ display:flex; flex-direction:column; gap:8px; min-width:0; }
.paywall__t{ margin:0; font-size:15px; font-weight:750; letter-spacing:-.012em; color:var(--ink); }
.paywall__p{ margin:0; font-size:13.5px; line-height:1.5; color:var(--ink-2); }
@media (max-width:480px){
  .paywall{ padding:15px; gap:11px; }
  .paywall__ico{ width:32px; height:32px; }
}

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{ animation-duration:.001ms !important; animation-iteration-count:1 !important; transition-duration:.001ms !important; scroll-behavior:auto !important; }
}

/* ============================================================================
   DEVICE PAGE  (#/model/<id>)
   ----------------------------------------------------------------------------
   A showcase, not a form. Three bands in the order a shop owner needs them:
   identity and price, the four specs that decide a repair quote, then the full
   sheet and compatibility.

   Everything is built on the shared tokens, so both themes come out of one set
   of rules — there is no colour here that exists only inside a dark block.
   ========================================================================== */

.dev-page{ padding-bottom:40px; }

/* -------------------------------------------------------------- sticky head
   The model name has to stay reachable through a long spec sheet, so the head
   pins under the app header rather than scrolling away with the hero. */
.dhead{
  /* Stacks BELOW the app header, which is itself sticky at top:0 with z-index
     60. Both at top:0 meant the topbar simply covered this one and the model
     name vanished the moment the page scrolled. */
  position:sticky; top:var(--topbar-h); z-index:30;
  background:var(--surface); border-bottom:1px solid var(--line);
  backdrop-filter:saturate(160%) blur(8px);
}
.dhead__in{
  max-width:1180px; margin:0 auto; padding:9px 16px;
  display:flex; align-items:center; gap:10px; min-height:56px;
}
.dhead__t{
  flex:1; min-width:0; font-family:var(--f-display); font-weight:800;
  font-size:15px; letter-spacing:-.02em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.dhead__cta{ flex:none; }

/* ---------------------------------------------------------------------- hero */
/* Chips carried over from the old hero: still used by the model page's
   meta row and its colour swatches. */
.dmeta{
  display:inline-flex; align-items:center; gap:6px;
  background:var(--surface-2); border:1px solid var(--line); border-radius:99px;
  padding:5px 11px; font-size:12.5px; font-weight:600; color:var(--ink-2);
}

.dmeta svg{ width:14px; height:14px; flex:none; }

.dsw__row{ display:flex; flex-wrap:wrap; gap:9px; }

.dsw{
  width:34px; height:34px; border-radius:11px; padding:0; cursor:pointer;
  background:var(--sw); border:1px solid rgba(0,0,0,.22);
  box-shadow:inset 0 -6px 10px -6px rgba(0,0,0,.45);
  transition:transform .12s ease, box-shadow .12s ease;
}

.dsw:hover{ transform:translateY(-1px); }

/* The ring is drawn OUTSIDE the swatch so it reads on a white finish as well
   as a black one — an inset ring disappears into a pale colour. */
.dsw.is-on{
  box-shadow:0 0 0 2px var(--surface), 0 0 0 4px var(--teal-500),
             inset 0 -6px 10px -6px rgba(0,0,0,.45);
}

.dsw:focus-visible{ outline:2px solid var(--teal-500); outline-offset:3px; }

/* ------------------------------------------------------------- key specs
   The four numbers a repair quote turns on. Scanned, never read. */
.dkeys{
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px;
  margin:24px 0 30px;
}
.dkey{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r);
  padding:14px 15px; min-width:0;
}
.dkey__i{ display:block; color:var(--teal-600); margin-bottom:9px; }
.dkey__i svg{ width:19px; height:19px; }
.dkey__l{
  display:block; font-size:10.5px; font-weight:700; letter-spacing:.09em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:3px;
}
.dkey__v{
  display:block; font-family:var(--f-display); font-weight:800; font-size:17px;
  letter-spacing:-.02em; color:var(--ink); line-height:1.2;
  overflow-wrap:anywhere;
}
.dkey__s{ display:block; font-size:12px; color:var(--ink-3); margin-top:3px; }

/* ------------------------------------------------------------ spec blocks */
.dsecs{ display:grid; gap:12px; margin-bottom:30px; }
.dsec{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r);
  overflow:hidden;
}
.dsec__h{
  display:flex; align-items:center; gap:9px; margin:0;
  padding:13px 16px; border-bottom:1px solid var(--line);
  background:var(--surface-2);
  font-family:var(--f-display); font-weight:800; font-size:13.5px;
  letter-spacing:-.01em; color:var(--ink);
}
.dsec__h svg{ width:16px; height:16px; color:var(--teal-600); flex:none; }
.dsec__b{ margin:0; padding:4px 16px 12px; }
.dspec{
  display:grid; grid-template-columns:minmax(96px,38%) 1fr; gap:14px;
  padding:9px 0; border-bottom:1px solid var(--line);
  align-items:baseline;
}
.dspec:last-child{ border-bottom:0; }
.dspec dt{ font-size:13px; color:var(--ink-3); min-width:0; overflow-wrap:anywhere; }
.dspec dd{ margin:0; font-size:13.5px; font-weight:600; color:var(--ink); min-width:0; overflow-wrap:anywhere; }

/* ----------------------------------------------------------- compatibility */
.dcompat{ margin-bottom:26px; }
.dcompat__p{ font-size:13.5px; margin:5px 0 14px; }

.dnote{
  display:flex; gap:9px; align-items:flex-start;
  font-size:12.5px; color:var(--ink-3); margin:0;
}
.dnote svg{ width:15px; height:15px; flex:none; margin-top:1px; }

/* ==========================================================================
   BREAKPOINTS
   Each size gets a layout, not a squeeze: the hero goes side-by-side only when
   there is room for the handset to stay large enough to read.
   ========================================================================== */

/* small phones — keep the handset visible without pushing the name off screen */
@media (max-width:399px){
  .dshot{ padding:18px 14px; min-height:210px; }
  .dshot__svg{ height:180px; }
  .dkeys{ gap:8px; }
  .dhead__cta span{ display:none; }      /* icon-only CTA, still 44px tall */
}

/* tablet and up: hero splits, spec sheet goes two-up */
@media (min-width:700px){
  .dhero{ grid-template-columns:minmax(0,300px) minmax(0,1fr); gap:30px; padding:30px 0 10px; }
  .dshot{ min-height:330px; padding:30px; }
  .dshot__svg{ height:280px; }
  .dkeys{ grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; }
  .dsecs{ grid-template-columns:repeat(2,minmax(0,1fr)); align-items:start; }
  .dhead__in{ padding:9px 20px; }
}

/* laptop: the shot can breathe */
@media (min-width:1024px){
  .dhero{ grid-template-columns:minmax(0,360px) minmax(0,1fr); gap:40px; padding:36px 0 14px; }
  .dshot{ min-height:400px; }
  .dshot__svg{ height:340px; }
  .dkey{ padding:16px 17px; }
}

/* wide desktop: three spec columns rather than a long two-column scroll */
@media (min-width:1320px){
  .dsecs{ grid-template-columns:repeat(3,minmax(0,1fr)); }
}

@media (prefers-reduced-motion:reduce){
  .dsw{ transition:none; }
  .dsw:hover{ transform:none; }
}

/* ============================================================================
   TOUCH TARGETS
   ----------------------------------------------------------------------------
   A finger needs about 44px; a mouse pointer does not. These rules grow the
   HIT area without changing the visual weight of the control, so the design is
   unchanged and the buttons simply become hittable.

   Two offenders the audit caught at 360px: the search input sat 27px tall
   inside a 56px field — the surrounding padding belonged to a plain div, so
   tapping it did nothing — and the "Open group" button was 22px.
   ========================================================================== */
@media (pointer:coarse), (max-width:900px){
  /* The search input carries no class, so it has to be matched by element —
     `.search .input` silently matched nothing and left it 27px tall. */
  .search input,
  .field input,
  .input{ min-height:44px; }
  .plate__cta{ min-height:40px; }
  .dsw{ width:38px; height:38px; }
  /* The breadcrumb is the back route on a phone — 22px tall is a miss. */
  .crumbs button{ min-height:40px; padding-inline:2px; }
}

/* ============================================================================
   BRAND DASHBOARD TILE
   ----------------------------------------------------------------------------
   The tile earns the space it takes: logo and name, then the counts in the
   order that matters when pricing a repair — phones first and largest, split
   flat/curved, then the other device classes.

   Cards are a fixed minimum height so a brand with no tablets does not sit
   half as tall as one that has them. Ragged card heights across a grid read as
   a broken layout, not as information.
   ========================================================================== */
.brandcard{
  display:flex; flex-direction:column; gap:10px; min-height:168px;
}
.brandcard__top{ display:flex; align-items:center; gap:11px; min-width:0; }
.brandcard__id{ display:flex; flex-direction:column; min-width:0; }
.brandcard__n{
  font-family:var(--f-display); font-weight:800; font-size:15px; letter-spacing:-.02em;
  color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.brandcard__c{ font-size:11.5px; color:var(--ink-3); }

/* the headline number */
.brandcard__lead{ display:flex; align-items:baseline; gap:6px; margin-top:auto; }
.brandcard__lead b{
  font-family:var(--f-display); font-weight:800; font-size:30px; line-height:1;
  letter-spacing:-.035em; color:var(--ink); font-variant-numeric:tabular-nums;
}
.brandcard__lead span{
  font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
  color:var(--ink-3);
}

/* Flat vs curved, drawn to scale. The curved remainder is the track itself, so
   the two segments cannot disagree with each other. */
.curvebar{
  display:block; height:5px; border-radius:99px; overflow:hidden;
  background:var(--b1, var(--teal-600)); opacity:.9;
}
.curvebar__flat{ display:block; height:100%; background:var(--ink-3); opacity:.55; }

.brandcard__split{ display:flex; gap:12px; flex-wrap:wrap; }
.brandcard__extra{
  display:flex; gap:12px; flex-wrap:wrap;
  padding-top:9px; border-top:1px solid var(--line);
}
.bcs{ font-size:11.5px; color:var(--ink-3); white-space:nowrap; }
.bcs b{
  font-weight:800; color:var(--ink-2); font-variant-numeric:tabular-nums;
  margin-right:2px;
}
/* A dot in each label ties the number to its half of the bar. */
.bcs--flat::before, .bcs--curved::before{
  content:''; display:inline-block; width:7px; height:7px; border-radius:99px;
  margin-right:5px; vertical-align:middle;
}
.bcs--flat::before{ background:var(--ink-3); opacity:.55; }
.bcs--curved::before{ background:var(--b1, var(--teal-600)); }

/* On the narrowest phones the tile drops the split bar rather than squeezing
   two labels onto one 150px line. */
@media (max-width:400px){
  .brandcard{ min-height:150px; }
  .brandcard__lead b{ font-size:26px; }
  .brandcard__split{ gap:8px; }
  .bcs{ font-size:11px; }
}

/* ============================================================================
   BRAND MODEL PAGE — header, controls, and three views
   ========================================================================== */

/* ------------------------------------------------------------------ header */
.bhead{
  display:flex; flex-wrap:wrap; gap:16px; align-items:center; justify-content:space-between;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);
  padding:16px; margin-bottom:14px;
}
.bhead__id{ display:flex; align-items:center; gap:13px; min-width:0; }
.bhead__n{
  font-family:var(--f-display); font-weight:800; font-size:22px;
  letter-spacing:-.03em; margin:0; color:var(--ink);
}
.bhead__c{ margin:2px 0 0; font-size:12.5px; color:var(--ink-3); }
/* Search and the view switch sit together on the right, as one tool group. */
.bhead__tools{ display:flex; align-items:center; gap:10px; margin-left:auto; flex-wrap:wrap; }
.bhead__q{ min-width:min(260px, 100%); }

/* View switch: three icons, not three words. The name is on the tooltip and
   the aria-label, so the control keeps its width in any language. */
.vswitch{
  display:flex; gap:2px; padding:3px; flex:none;
  background:var(--surface-3); border:1px solid var(--line); border-radius:11px;
}
.vswitch__b{
  display:grid; place-items:center; width:36px; height:34px; padding:0;
  border:0; border-radius:8px; background:transparent; color:var(--ink-3); cursor:pointer;
  transition:background .15s, color .15s;
}
.vswitch__b svg{ width:17px; height:17px; }
.vswitch__b:hover{ color:var(--ink-2); background:var(--surface); }
.vswitch__b.is-on{ background:var(--surface); color:var(--teal-700); box-shadow:var(--sh-1); }
.vswitch__b:focus-visible{ outline:2px solid var(--teal-500); outline-offset:1px; }

/* ---------------------------------------------------------------- controls */
.bctl{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-bottom:10px; }
.fsel{ position:relative; display:inline-flex; }
.fsel__l{
  position:absolute; left:11px; top:-7px; padding:0 4px; z-index:1;
  background:var(--paper); font-size:9.5px; font-weight:800; letter-spacing:.07em;
  text-transform:uppercase; color:var(--ink-3); pointer-events:none;
}
.fsel select{
  appearance:none; -webkit-appearance:none;
  min-height:40px; padding:0 30px 0 11px;
  background:var(--surface); color:var(--ink);
  border:1px solid var(--line); border-radius:10px;
  font:inherit; font-size:13px; font-weight:600; cursor:pointer;
  /* chevron drawn in CSS so it inherits the theme ink colour */
  background-image:linear-gradient(45deg, transparent 50%, currentColor 50%),
                   linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position:calc(100% - 15px) 18px, calc(100% - 11px) 18px;
  background-size:4px 4px, 4px 4px;
  background-repeat:no-repeat;
}
.fsel select:hover{ border-color:var(--ink-3); }
.fsel select:focus-visible{ outline:2px solid var(--teal-500); outline-offset:1px; }
/* An applied filter must be visible at a glance, or a shortened list reads as
   missing data rather than as a filtered one. */
.fsel.is-set select{ border-color:var(--teal-500); color:var(--teal-700); font-weight:700; }
.bctl__clear{ margin-left:auto; }
.bcount{ font-size:12.5px; color:var(--ink-3); margin:0 0 12px; font-variant-numeric:tabular-nums; }

/* ------------------------------------------------------- shared device art */
.dvc{ display:block; width:auto; height:100%; filter:drop-shadow(0 6px 12px rgba(0,0,0,.22)); }
.dvc--xs{ height:30px; filter:none; }

/* --------------------------------------------------------------- grid view */
.dgrid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:12px; }
@media (min-width:560px){ .dgrid{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
@media (min-width:900px){ .dgrid{ grid-template-columns:repeat(4,minmax(0,1fr)); } }
@media (min-width:1280px){ .dgrid{ grid-template-columns:repeat(5,minmax(0,1fr)); } }

.dcard{
  display:flex; flex-direction:column; text-align:left; padding:0; overflow:hidden;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);
  cursor:pointer; transition:transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .18s, border-color .18s;
}
.dcard:hover{ transform:translateY(-3px); box-shadow:var(--sh-2); border-color:var(--teal-500); }
.dcard:focus-visible{ outline:2px solid var(--teal-500); outline-offset:2px; }
/* The device image leads — showing it instead of a repeated brand logo is the
   whole reason this view exists. */
.dcard__shot{
  display:grid; place-items:center; height:132px; padding:14px 10px;
  background:radial-gradient(120% 90% at 50% 0%, var(--teal-050), transparent 70%), var(--surface-2);
  border-bottom:1px solid var(--line);
}
.dcard__b{ display:flex; flex-direction:column; gap:5px; padding:11px 12px 13px; min-width:0; }
.dcard__n{
  font-weight:700; font-size:13.5px; color:var(--ink); line-height:1.25;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.dcard__m{ font-size:11.5px; color:var(--ink-3); font-variant-numeric:tabular-nums; }
.dcard__t{ display:flex; gap:5px; flex-wrap:wrap; margin-top:2px; }

.tag{
  font-size:10px; font-weight:800; letter-spacing:.05em; text-transform:uppercase;
  padding:2.5px 7px; border-radius:99px;
  background:var(--surface-3); color:var(--ink-3); white-space:nowrap;
}
.tag--curved{ background:var(--teal-100); color:var(--teal-700); }
.tag--parts{ background:var(--teal-100); color:var(--teal-700); }

/* --------------------------------------------------------------- list view */
.dlist{ display:flex; flex-direction:column; gap:8px; }
.drow{
  display:flex; align-items:center; gap:13px; text-align:left; width:100%;
  padding:10px 13px; background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r); cursor:pointer; transition:border-color .15s, background .15s;
}
.drow:hover{ border-color:var(--teal-500); background:var(--surface-2); }
.drow:focus-visible{ outline:2px solid var(--teal-500); outline-offset:2px; }
.drow__shot{ display:grid; place-items:center; width:38px; height:52px; flex:none; }
.drow__main{ display:flex; flex-direction:column; gap:2px; min-width:0; flex:1; }
.drow__n{
  font-weight:700; font-size:14px; color:var(--ink);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.drow__m{
  font-size:12px; color:var(--ink-3);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.drow__side{ display:flex; align-items:center; gap:8px; flex:none; }
.drow__y{ font-size:12px; color:var(--ink-3); font-variant-numeric:tabular-nums; }

/* -------------------------------------------------------------- table view */
.dtable-wrap{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-lg); overflow:hidden;
}
.dtable{ width:100%; border-collapse:collapse; font-size:13px; }
.dtable th{
  text-align:left; padding:10px 13px; white-space:nowrap;
  background:var(--surface-2); border-bottom:1px solid var(--line);
  font-size:10px; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  color:var(--ink-3);
}
.dtable td{
  padding:9px 13px; border-bottom:1px solid var(--line);
  color:var(--ink-2); vertical-align:middle; white-space:nowrap;
}
.dtable tbody tr{ cursor:pointer; transition:background .12s; }
.dtable tbody tr:hover{ background:var(--surface-2); }
.dtable tbody tr:focus-visible{ outline:2px solid var(--teal-500); outline-offset:-2px; }
.dtable tbody tr:last-child td{ border-bottom:0; }
.tcell-dev{ display:flex; align-items:center; gap:10px; font-weight:700; color:var(--ink); }
.c-device{ min-width:190px; }

/* Columns drop from the least useful end as the viewport narrows, so the table
   never needs a horizontal scrollbar. Hiding in CSS rather than in JS means a
   resize costs nothing — there is no re-render. */
@media (max-width:1499px){ .dtable .c-res{ display:none; } }
@media (max-width:1399px){ .dtable .c-camera{ display:none; } }
@media (max-width:1279px){ .dtable .c-ram, .dtable .c-storage{ display:none; } }
@media (max-width:1099px){ .dtable .c-chipset{ display:none; } }
@media (max-width:999px){ .dtable .c-network{ display:none; } }

/* Below 900 the table stops being a table. A grid this wide on a phone can
   only scroll sideways, which hides exactly the column someone is looking for,
   so each row becomes a card and every cell carries its own label. */
@media (max-width:899px){
  .dtable thead{ display:none; }
  .dtable, .dtable tbody, .dtable tr, .dtable td{ display:block; width:100%; }
  .dtable-wrap{ background:transparent; border:0; border-radius:0; }
  .dtable tr{
    background:var(--surface); border:1px solid var(--line); border-radius:var(--r);
    margin-bottom:8px; padding:6px 2px;
  }
  .dtable td{
    display:flex; justify-content:space-between; align-items:center; gap:14px;
    border-bottom:0; padding:6px 13px; white-space:normal;
  }
  .dtable td::before{
    content:attr(data-label);
    font-size:10.5px; font-weight:800; letter-spacing:.07em; text-transform:uppercase;
    color:var(--ink-3); flex:none;
  }
  .dtable .c-device{ min-width:0; padding-top:9px; }
  .dtable .c-device::before{ display:none; }     /* the name is its own label */
  .dtable .c-battery, .dtable .c-chipset, .dtable .c-network,
  .dtable .c-ram, .dtable .c-storage{ display:flex; }
}

/* ------------------------------------------------------------ small screens */
@media (max-width:640px){
  .bhead{ padding:13px; gap:12px; }
  .bhead__tools{ width:100%; margin-left:0; }
  .bhead__q{ flex:1; min-width:0; }
  .bhead__n{ font-size:19px; }
  /* the filter bar scrolls sideways rather than stacking into a wall */
  .bctl{
    flex-wrap:nowrap; overflow-x:auto; padding:8px 0 4px;
    scrollbar-width:none; -webkit-overflow-scrolling:touch;
  }
  .bctl::-webkit-scrollbar{ display:none; }
  .fsel select{ min-height:44px; }
  .bctl__clear{ margin-left:0; flex:none; }
  .dcard__shot{ height:112px; }
}

@media (prefers-reduced-motion:reduce){
  .dcard, .drow, .vswitch__b, .dtable tbody tr{ transition:none; }
  .dcard:hover{ transform:none; }
}

/* ============================================================================
   DEVICE PAGE — hero balance and the variant picker
   ----------------------------------------------------------------------------
   The highlight cards used to sit below the fold while the right half of the
   hero stood empty. They are part of the hero now, in a three-column band, so
   the specs that decide a repair quote are the first thing read.
   ========================================================================== */

/* A real content container. Without this the page ran to the browser edge on
   a wide window and looked unfinished. */
.dev-page .wrap{
  max-width:1360px; margin-inline:auto;
  padding-inline:clamp(16px, 4vw, 32px);
}

/* ------------------------------------------------------------ highlights */
.dkeys{ grid-template-columns:repeat(2,minmax(0,1fr)); }
@media (min-width:700px){ .dkeys{ grid-template-columns:repeat(3,minmax(0,1fr)); } }
/* Six cards over two rows of three keeps every card the same height; four
   across leaves a two-card orphan row on a wide screen. */
@media (min-width:1024px){ .dkeys{ grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px; } }
.dkey__s{ line-height:1.4; }

/* --------------------------------------------------------- variant picker */
.vpick{
  display:flex; flex-direction:column; gap:9px;
  padding:12px; margin-bottom:0;
  background:var(--surface-2); border:1px solid var(--line); border-radius:var(--r);
}
.vpick__row{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.vpick__l{
  font-size:10px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-3); width:52px; flex:none;
}
.vpick__chips{ display:flex; gap:6px; flex-wrap:wrap; }

.vchip{
  min-height:36px; padding:0 13px; cursor:pointer;
  background:var(--surface); color:var(--ink-2);
  border:1px solid var(--line); border-radius:9px;
  font:inherit; font-size:12.5px; font-weight:700;
  font-variant-numeric:tabular-nums;
  transition:border-color .14s, color .14s, background .14s;
}
.vchip:hover:not(:disabled){ border-color:var(--ink-3); color:var(--ink); }
.vchip.is-on{
  border-color:var(--teal-500); background:var(--teal-100);
  color:var(--teal-700); box-shadow:inset 0 0 0 1px var(--teal-500);
}
/* A pair that is not sold stays visible but dead — hiding it would make the
   row jump every time RAM changes. */
.vchip:disabled{ opacity:.4; cursor:not-allowed; text-decoration:line-through; }
.vchip:focus-visible{ outline:2px solid var(--teal-500); outline-offset:2px; }

@media (max-width:520px){
  .vpick__l{ width:100%; }
  .vstock{ margin-left:0; width:100%; }
  .vchip{ min-height:40px; }
}

@media (prefers-reduced-motion:reduce){ .vchip{ transition:none; } }

/* Boot placeholder — shown only while assets/dataset.json downloads. */
.bootwait{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:14px; min-height:70vh; color:var(--ink-3); font-size:13.5px;
}
.bootwait svg{ opacity:.85; }

/* Shown on the account screen while Firebase is still answering. It is not the
   sign-in form on purpose: "still loading" and "signed out" are different
   states, and drawing the second while the first is true is what a returning
   user reads as being sent back to the login page.

   The icon needs an explicit size because SM.icon() emits a viewBox and no
   width — without one the svg fills its flex row and the card becomes a giant
   arrow. */
.authwait{ display:flex; align-items:center; gap:14px; }
.authwait svg{
  flex:none; width:22px; height:22px; color:var(--teal-600);
  animation:authwait-spin 1.1s linear infinite;
}
@keyframes authwait-spin{ to{ transform:rotate(360deg); } }
@media (prefers-reduced-motion:reduce){
  .authwait svg{ animation:none; }
}

/* The already-authenticated Google account, shown on the profile-completion
   form. It is a statement, not a control: the user has chosen this account and
   asking them to choose again is the step this screen removes. */
.gacct{
  display:flex; align-items:center; gap:12px; margin:14px 0 4px; padding:12px 14px;
  background:var(--surface-2); border:1px solid var(--line); border-radius:var(--r);
}
.gacct__av{
  width:38px; height:38px; flex:none; border-radius:99px; overflow:hidden;
  display:grid; place-items:center;
  background:var(--teal-100); color:var(--teal-700);
  font-weight:800; font-size:14px;
}
.gacct__av img{ width:100%; height:100%; object-fit:cover; display:block; }
.gacct__b{ display:flex; flex-direction:column; min-width:0; flex:1; }
.gacct__l{
  font-size:10px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-3);
}
/* Wrapped, not truncated: this card exists so the shop can see WHICH account
   it is finishing, and "jeevananthamsathiy…" does not answer that. */
.gacct__e{
  font-size:14px; font-weight:600; color:var(--ink);
  overflow-wrap:anywhere; line-height:1.35;
}
.gacct > svg{ width:18px; height:18px; color:var(--teal-600); flex:none; }

/* ---------------------------------------------------------- sign-up form
   The address accordion. Optional details should look optional: five address
   inputs standing permanently open read as five more things that must be
   filled in, which is exactly the impression the mandatory fields need to not
   compete with. */
.addrfold{ margin-top:4px; border-top:1px solid var(--line); padding-top:12px; }
.addrfold__t{
  display:flex; align-items:center; gap:10px; width:100%;
  background:none; border:0; padding:10px 2px; cursor:pointer;
  font:inherit; font-weight:650; font-size:14px; color:var(--ink-1); text-align:left;
}
.addrfold__t svg{ width:16px; height:16px; flex:none; color:var(--teal-600); }
.addrfold__t:hover{ color:var(--teal-600); }
.addrfold__b{ padding-top:4px; }

/* The primary action, kept in reach. It sticks to the bottom of the scrolling
   card rather than to the viewport, so it cannot cover an input the user is
   typing into, and it clears the mobile tab bar and the home indicator. */
.regcta{
  position:sticky; bottom:0; z-index:3;
  /* Negative margins pull it to the card's edges so the fade reaches them;
     the values mirror .card--pad, which is 18px and 22px from 768px up. */
  margin:16px -18px -18px; padding:14px 18px;
  padding-bottom:calc(14px + env(safe-area-inset-bottom, 0px));
  background:linear-gradient(to bottom, rgba(0,0,0,0), var(--surface) 22%);
  border-radius:0 0 var(--r-lg) var(--r-lg);
}
@media (min-width:768px){
  .regcta{ margin:18px -22px -22px; padding:16px 22px; }
}
/* The button fills the bar and never more. .btn is nowrap, so a label that
   outgrows a narrow phone is clipped inside the button instead of pushing it
   past the card's edge. */
.regcta .btn{ max-width:100%; min-width:0; overflow:hidden; }
@media (max-width:900px){
  /* Clear of the bottom navigation, which is fixed over the page down here. */
  .regcta{ bottom:var(--tabbar-h, 0px); }
}

/* The line under the form that says what is still missing, or that everything
   is complete. It carries an icon, and `img,svg{display:block}` in styles.css
   would otherwise drop the sentence onto the line beneath it. */
#regHint{
  display:flex; align-items:flex-start; gap:7px;
  margin-top:12px; line-height:1.45;
}
#regHint svg{ width:15px; height:15px; flex:none; margin-top:1px; color:var(--ok); }

/* The sign-in / sign-up error and status line. It sits between the form and
   the Google button, so an empty one must take no room at all — a bare
   #authMsg with padding left a gap that read as a missing field. */
#authMsg:empty{ display:none; }

/* A spec the source workbook has a column for but no value for on this device.
   Drawn rather than dropped, because "we have not recorded this yet" is a fact
   the person enriching the catalogue needs to see. Muted so a page of real
   values still reads as a page of real values. */
.dspec.is-unknown dd{ color:var(--ink-3); font-variant-numeric:normal; }

/* A launch price that has not been recorded. Shown as ₹0 rather than hidden,
   so the field is visibly waiting for the enrichment pass — but muted, so it
   never reads as a real price somebody could quote. */
.dprice--unknown .dprice__n{ color:var(--ink-3); font-weight:600; }

/* "Not recorded" in a table cell. Muted so a column of real values still scans
   as real values, and a column of hyphens reads as a gap to fill. */
.c-none{ color:var(--ink-3); }

/* ---- share fallback ------------------------------------------------------
   Only ever built where the browser has no share sheet of its own, so this is
   a desktop shape: four plain links out, sized for a pointer. */
.sharegrid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; }
@media (min-width:420px){ .sharegrid{ grid-template-columns:repeat(4,minmax(0,1fr)); } }
.sharetile{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:7px;
  padding:14px 8px; border-radius:var(--r-sm); text-align:center;
  background:var(--surface-2); border:1px solid var(--line); color:var(--ink);
  font-size:12px; font-weight:700;
  transition:background .14s, border-color .14s, transform .14s;
}
.sharetile:hover{ background:var(--teal-050); border-color:var(--teal-100); color:var(--teal-700); transform:translateY(-1px); }
.sharetile svg{ width:19px; height:19px; }

/* The device grid, list rows, table name cell and search result cards are
   <a href> rather than <button>: that is what makes a model page reachable by
   a crawler and by a middle-click. A button carries no colour or underline of
   its own, so those are reset here rather than in every rule above. */
.dcard, .drow, .mcard, .tcell-dev{ text-decoration:none; color:inherit; }
.dcard:hover, .drow:hover, .mcard:hover, .tcell-dev:hover{ text-decoration:none; }
.tcell-dev{ color:inherit; }

/* ==========================================================================
   MODEL PAGE — photograph, identity, specifications

   Three columns on a desktop. The photograph is the widest because it is the
   only column whose content cannot be summarised; identity carries the name,
   the price and the controls; specifications is the long column.

   Everything below is new structure only. The chips, swatches, highlight cards
   and spec rows keep the classes and the styling they already had.
   ========================================================================== */

.dv3{
  display:grid;
  grid-template-columns:minmax(0,1.35fr) minmax(0,1fr) minmax(0,1fr);
  gap:26px;
  align-items:start;
  margin:14px 0 10px;
}

/* Sticky on the MEDIA column, not on the specs. The spec sheet is the long
   content — pinning that would fight the scroll it wants to do — whereas
   keeping the handset on screen while the specs go past is the point. */
@media (min-width:1100px){
  .dv3__media{ position:sticky; top:88px; align-self:start; }
}

/* ------------------------------------------------------------------ gallery

   No aspect-ratio is imposed on the image. A tablet is not a tall phone and a
   watch is neither, so the box is bounded and object-fit:contain keeps
   whatever ratio the file actually has. The cap is in vh so a portrait shot
   can never push the model's name below the fold. */
.dgal{ display:flex; flex-direction:column; gap:12px; }

.dgal__main{
  display:flex; align-items:center; justify-content:center;
  min-height:400px; max-height:62vh; padding:24px;
  background:
    radial-gradient(120% 90% at 50% 0%, var(--teal-050), transparent 70%),
    var(--surface-2);
  border:1px solid var(--line); border-radius:var(--r-lg);
  overflow:hidden;
}
/* The wrapper has to span the panel. It is an inline-flex span, so left to
   itself it shrinks to the image's own 160px — and then the image's
   width:min(100%,320px) resolves that 100% against 160px and never grows. */
.dgal__main .dphoto{ width:100%; max-height:100%; }
/* The catalogue's images are a uniform 160x212 — the device drawn inside a
   fixed box, whatever it is. Rendered at that size they sit in the middle of a
   500px panel looking like an afterthought; rendered to the full panel they go
   soft. 320px is a shade under 2x, which these flat studio renders carry
   without visible softening, and it fills the column.

   width, not height: every source image has the same ratio, so constraining
   the width is enough and height:auto keeps it undistorted. */
.dgal__main .dphoto__img{
  width:min(100%, 320px); height:auto;
  max-height:calc(62vh - 48px); object-fit:contain;
  filter:drop-shadow(0 16px 30px rgba(0,0,0,.22));
}
.dgal__main .dphoto__art{ height:230px; width:auto; max-width:100%; }

/* The manufacturer's photograph leads; the drawn handset is the fallback for
   the sixty devices with no image URL and for a URL that fails. Both are in
   the HTML because onerror fires long after the markup is written, so the
   fallback has to already be there for CSS to reveal. */
.dphoto{ position:relative; display:inline-flex; align-items:center; justify-content:center; }
.dphoto .dphoto__art{ display:none; }
.dphoto.is-failed .dphoto__img{ display:none; }
.dphoto.is-failed .dphoto__art,
.dphoto--art .dphoto__art{ display:block; }

/* Architecture for front / back / side / colour shots. Not drawn at all while
   there is one image — a strip of empty placeholders is worse than no strip. */
.dgal__rail{ display:flex; gap:8px; flex-wrap:wrap; }
.dthumb{
  width:64px; height:64px; padding:7px; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-sm); transition:border-color .14s, box-shadow .14s;
}
.dthumb:hover{ border-color:var(--ink-3); }
.dthumb.is-on{ border-color:var(--teal-500); box-shadow:inset 0 0 0 1px var(--teal-500); }
.dthumb:focus-visible{ outline:2px solid var(--teal-500); outline-offset:2px; }
.dthumb .dphoto__img,
.dthumb .dphoto__art{ max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain; }

/* ----------------------------------------------------------------- identity */
/* The gap is tight on purpose. Everything in this column — name, date,
   price, highlights, the three selectors and the Find parts button — has to
   fit above the fold on a 900px-tall laptop, which is the whole point of
   moving the specs out into their own column. */
.dv3__id{ display:flex; flex-direction:column; gap:11px; min-width:0; }

.did__brand{
  display:flex; align-items:center; gap:9px;
  font-size:13.5px; font-weight:600; color:var(--ink-2);
}
.did__name{
  font-family:var(--f-display); font-weight:800;
  font-size:clamp(23px,2.5vw,32px); line-height:1.08; letter-spacing:-.03em;
  margin:0; text-wrap:balance; color:var(--ink); overflow-wrap:anywhere;
}
.did__meta{ display:flex; flex-wrap:wrap; gap:7px; }

/* Price as a card rather than a line: it is a value that changes when a chip
   is clicked, and it needs to read as one thing when it says a number and
   when it says the number is not recorded. Never a nought — see priceHTML. */
.dprice{
  display:flex; flex-direction:column; gap:2px; align-items:flex-start;
  padding:10px 14px; margin:0;
  background:var(--surface-2); border:1px solid var(--line); border-radius:var(--r);
}
.dprice__l{
  font-size:10px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-3);
}
.dprice__n{
  font-family:var(--f-display); font-weight:800; font-size:27px;
  letter-spacing:-.025em; color:var(--ink); font-variant-numeric:tabular-nums;
}
.dprice__v{ font-size:12.5px; font-weight:600; color:var(--ink-2); font-variant-numeric:tabular-nums; }
.dprice__s{ display:inline-flex; align-items:center; gap:5px; font-size:12px; color:var(--ink-3); }
.dprice__s svg{ width:13px; height:13px; }
.dprice--none .dprice__na{ font-size:16px; font-weight:700; color:var(--ink-3); }

/* The highlight cards sit in the identity column now, one per line on a narrow
   column and two across when there is room. */
/* Three across rather than two: the same six facts in two rows instead of
   three, which is what buys the CTA its place above the fold. */
.dv3 .dkeys{ grid-template-columns:repeat(auto-fill,minmax(112px,1fr)); gap:8px; margin:0; }
.dv3 .dkey{ padding:10px 11px; }
.dv3 .dkey__i{ margin-bottom:6px; }

/* The selected colour's name, beside its swatches. */
.vpick__name{ font-size:12.5px; font-weight:600; color:var(--ink-2); }

/* The handset beside the Body heading — a footnote to the dimensions. Pushed
   to the end of the header row and kept small and quiet on purpose. */
.dsec__art{ margin-left:auto; display:flex; align-items:center; opacity:.85; }
.dsec__art .dphoto__img,
.dsec__art .dphoto__art{ height:34px; width:auto; max-width:34px; object-fit:contain; }

.did__cta{ display:flex; flex-direction:column; align-items:stretch; gap:7px; }
.did__cta .btn{ width:100%; justify-content:center; }
.did__note{ font-size:12.5px; color:var(--ink-3); text-align:center; }

/* ----------------------------------------------------------- specifications */
.dv3__specs{ display:flex; flex-direction:column; gap:12px; min-width:0; }
.dv3__h{
  font-size:10px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-3); margin:0 0 2px;
}

/* ------------------------------------------------------------------ tablet
   Two columns: the handset beside its identity, specifications full width
   under both. Three columns at this width leaves each too narrow to read. */
@media (max-width:1099px){
  .dv3{ grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:22px; }
  .dv3__specs{ grid-column:1 / -1; }
  .dgal__main{ min-height:340px; max-height:52vh; }
  .dgal__main .dphoto__img{ width:min(100%, 280px); max-height:calc(52vh - 48px); }
}

/* Once the spec block is full width, two columns of sections — otherwise a
   long sheet becomes one very tall ribbon with empty space beside it. */
@media (min-width:760px) and (max-width:1099px){
  .dv3__specs{ display:grid; grid-template-columns:1fr 1fr; gap:12px; align-items:start; }
  .dv3__h{ grid-column:1 / -1; }
}

/* ------------------------------------------------------------------ mobile
   One column, in the order the DOM already has: image, brand, name, date,
   price, highlights, colour, RAM, storage, Find parts, specifications. */
@media (max-width:759px){
  .dv3{ grid-template-columns:minmax(0,1fr); gap:18px; }
  .dgal__main{ min-height:0; max-height:46vh; padding:16px; }
  .dgal__main .dphoto__img{ width:min(100%, 240px); max-height:calc(46vh - 32px); }
  .dgal__main .dphoto__art{ height:200px; }
  .dthumb{ width:54px; height:54px; }
}

/* ==========================================================================
   COMPATIBILITY GROUPS — card previews and the inline group view

   A group used to open in a sheet over the page. It opens in the centre column
   now, so the categories on the left and the selected device on the right stay
   put. Nothing here is an overlay.
   ========================================================================== */

/* ------------------------------------------------------- card device strip
   Replaces the five-bar sparkline (a picture of one number) and the line
   "N more devices — not listed" (which said the catalogue lacks devices it
   has). Five real handsets, from the group's own membership. */
.gprev{
  display:flex; gap:8px; align-items:flex-start;
  margin-top:10px; overflow:hidden;
}
.gprev__i{
  flex:1 1 0; min-width:0; display:flex; flex-direction:column; align-items:center; gap:5px;
  padding:8px 4px 7px; border:1px solid var(--line); border-radius:var(--r-sm);
  background:var(--surface); text-align:center;
}
.gprev__i.is-master{ border-color:var(--c, var(--teal-500)); }
.gprev .dphoto{ height:44px; display:flex; align-items:center; justify-content:center; }
.gprev__ph .dphoto__img,
.gprev__i .dphoto__img{ height:44px; width:auto; max-width:100%; object-fit:contain; }
.gprev__i .dphoto__art{ height:44px; width:auto; }
.gprev__n{
  font-size:10.5px; line-height:1.25; color:var(--ink-3);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; word-break:break-word;
}
/* Four on a narrow card, five when there is room; the fifth is simply hidden
   rather than squeezed to an unreadable width. */
@media (max-width:420px){ .gprev__i:nth-child(n+4){ display:none; } }

/* The back control of the open group's header. */
.gback{
  flex:none; display:inline-flex; align-items:center; justify-content:center; gap:5px;
  height:36px; padding:0 9px; cursor:pointer;
  background:var(--surface); color:var(--ink-2);
  border:1px solid var(--line); border-radius:999px;
  font:inherit; font-size:13px; font-weight:700;
  transition:border-color .14s, color .14s, background .14s;
}
.gback:hover{ border-color:var(--teal-500); color:var(--teal-700); background:var(--surface-2); }
.gback:focus-visible{ outline:2px solid var(--teal-500); outline-offset:2px; }
.gback svg{ width:17px; height:17px; }

/* ==========================================================================
   DEVICES IN THIS GROUP — compact chips, organised by brand

   The list used to be one full-width row per device with a radio circle on the
   left: 325 rows, each 39px tall and 900px wide, to carry one line of text.
   That is twelve screens of scrolling to read a list that fits on two, and a
   radio drawn beside a row whose own background already says it is selected is
   a second control for one piece of state.

   What replaced it is a chip — the device's own photograph at favicon size and
   its full name — in a grid that takes as many columns as the column it is in
   can hold. The brand headings are what make it navigable: nobody reads 325
   names in sequence, but "Vivo, 109" is a place you can scroll to.
   ========================================================================== */
.glist{ padding-bottom:10px; }

/* The brand blocks carried content-visibility:auto for a while, and it is
   worth writing down why they no longer do.

   It did what it promised — about 40ms off the first paint of the largest
   group in the catalogue, 325 devices across thirteen brands. What it also did
   was change the page's height AFTER the paint, as blocks came in and out of
   the skipped state and swapped their real heights for the estimate in
   contain-intrinsic-size. A list whose total height moves by a hundred pixels a
   third of a second late is a list you cannot scroll to a position in: the
   phone's jump to a picked device's specifications landed correctly and then
   slid a header's worth off it.

   40ms on the biggest group in the database — the median group holds three
   devices — is not worth a scroll that cannot be relied on. The saving that
   actually mattered was never the layout anyway: it is loading="lazy" on 325
   photographs, and that is still there. */
.gbrand + .gbrand{ margin-top:14px; }

/* A plain heading in the flow. It used to stick under the group's header;
   the header is no longer sticky, and the device toolbar is what stays. */
.gbrand__h{
  position:static;
  display:flex; align-items:center; gap:8px;
  margin:0 0 7px; padding:7px 0 6px;
  background:var(--paper);
  font-family:var(--f-ui); font-size:11.5px; font-weight:800;
  letter-spacing:.06em; text-transform:uppercase; color:var(--ink-3);
}
.gbrand__n{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.gbrand__c{
  flex:none; font-size:11px; font-weight:700; letter-spacing:0;
  color:var(--ink-3); background:var(--surface-2);
  border:1px solid var(--line); border-radius:999px; padding:1px 8px;
}
/* The brand mark, at the size of the heading it sits in. */
.blogo--chip{ width:22px; height:22px; border-radius:6px; font-size:9px; flex:none; }
.blogo--chip.blogo--img,
.blogo--chip.blogo--mark,
.blogo--chip.blogo--word{ padding:2px; }

/* auto-fill, not auto-fit: an eight-device brand in a wide column should be
   eight chips across the top of its block, not eight chips stretched to fill
   the row. minmax's floor is the width a full model name needs before it has
   to be truncated. */
.gchips{
  display:grid; gap:5px;
  grid-template-columns:repeat(auto-fill,minmax(196px,1fr));
}

.gchip{
  display:flex; align-items:center; gap:9px; width:100%; text-align:left;
  min-width:0; padding:5px 10px 5px 5px; cursor:pointer;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-sm);
  font:inherit; color:var(--ink);
  transition:background .12s, border-color .12s, box-shadow .12s;
}
.gchip:hover{ border-color:var(--teal-500); background:var(--surface-2); }
.gchip:focus-visible{ outline:2px solid var(--teal-500); outline-offset:1px; }
/* The whole chip is the selected state. No dot, no circle, no radio. */
.gchip.is-on{
  background:var(--teal-050); border-color:var(--teal-500);
  box-shadow:inset 0 0 0 1px var(--teal-500);
}
.gchip.is-master{ border-color:var(--amber-400); background:var(--amber-050); }
.gchip.is-master.is-on{ border-color:var(--teal-500); background:var(--teal-050); }
/* The device that was searched for, where a match card previews the group it
   was found in. Marked, but quietly: the card is already about that model. */
.gchip.is-hit{ border-color:var(--teal-500); }

/* The handset, at favicon size, on the white ground these cut-outs are shot
   against. One border, on the chip, not a second one around the picture. */
.gchip__ph{
  position:relative; flex:none; width:30px; height:30px;
  display:flex; align-items:center; justify-content:center;
  background:var(--ctile-paper, #FFF); border-radius:6px; overflow:hidden;
}
.gchip__img{ max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain; display:block; }
/* The stand-in for the 60 devices with no photograph, and for a URL that
   fails: a handset outline drawn in CSS, not a 40-node SVG per chip. */
.gchip__fb{ display:none; width:13px; height:20px; border:1.5px solid var(--ink-3); border-radius:3px; opacity:.5; }
.gchip__ph.is-failed .gchip__img{ display:none; }
.gchip__ph.is-failed .gchip__fb{ display:block; }

.gchip__n{
  flex:1 1 auto; min-width:0; font-size:13px; font-weight:600; line-height:1.25;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.gchip.is-on .gchip__n{ font-weight:700; color:var(--teal-700); }

.gchip__flag{ flex:none; display:inline-flex; color:var(--amber-700); }
.gchip__flag svg{ width:13px; height:13px; }
.gchip__flag--hit{ color:var(--teal-600); }

/* ==========================================================================
   IMAGE-FIRST CARDS — the open group's device grid

   The horizontal chip put a 30px photograph beside a name in a cell 190px
   wide, so most of every cell was the empty right-hand side of a short name.
   The card turns that around: the photograph fills the top of the card and
   the name sits centred under it, so the width a card has is spent on the
   thing a repairer recognises first.

   Same element as the chip (.gchip), reshaped by --card. Every existing rule
   about selection, the master and the searched-for device still applies.
   Selectors are doubled (.gchip.gchip--card) so the chip's narrow-screen
   overrides further down, which are single-class, cannot win over them.
   ========================================================================== */
.glist .gbrand + .gbrand{ margin-top:24px; }
.glist .gbrand__h{ margin-bottom:10px; }

/* THE COLUMN COUNT, IN ONE LINE.

   The narrowest a column may be is the LARGER of two floors:
     --gc-min            the width a card needs for its photo and a name, and
     (100% - gaps) / N   the width that would put exactly N across.
   While the column is wide the second floor is the larger, so there are never
   more than --gc-cols across; as it narrows, --gc-min takes over and
   auto-fill drops a column each time one more no longer fits — 6, 5, 4, 3, 2,
   and 1 only when two genuinely cannot stand side by side.

   Every percentage here is of the GRID's own width, which is the centre column
   minus its padding — never the viewport. The panels either side are already
   subtracted by the time this is measured, so a wider left panel or a closed
   right one changes the count on its own.

   auto-fill rather than auto-fit keeps the empty tracks, so a brand with one
   or two models gets one or two card-sized cards, not two cards stretched
   across the row. */
.gchips.gchips--cards{
  --gc-min:156px;
  --gc-cols:6;
  --gc-gap:12px;
  gap:var(--gc-gap);
  grid-template-columns:repeat(auto-fill,
    minmax(max(var(--gc-min), calc((100% - (var(--gc-cols) - 1) * var(--gc-gap)) / var(--gc-cols))), 1fr));
}

.gchip.gchip--card{
  position:relative;
  flex-direction:column; align-items:stretch; justify-content:flex-start; gap:9px;
  height:100%; padding:8px 8px 11px;
  text-align:center; border-radius:var(--r);
}
.gchip.gchip--card:hover{ box-shadow:var(--sh-1); }

/* The photograph gets the top of the card and a fixed height, so a lazy image
   arriving late fills space that was already reserved and nothing reflows —
   the same reason every card in a row is exactly the same height. */
.gchip--card .gchip__ph{ width:100%; height:104px; padding:8px; border-radius:var(--r-xs); }
.gchip--card .gchip__fb{ width:30px; height:48px; border-radius:6px; }

/* Centred, two lines at most, and two lines of room ALWAYS: a card whose name
   fits on one line keeps the same height as its neighbour whose name needs
   two, so rows stay level and the name sits the same distance under every
   photograph. The full name is in the card's title for the few that clamp. */
.gchip--card .gchip__n{
  flex:none; display:-webkit-box; -webkit-box-orient:vertical;
  -webkit-line-clamp:2; line-clamp:2;
  white-space:normal; text-overflow:clip; overflow:hidden; overflow-wrap:break-word;
  min-height:2.6em; padding:0 4px;
  font-size:13px; font-weight:600; line-height:1.3;
}

/* The crown and the tick move onto the photograph's corner, where they mark
   the card without taking a line from the name. */
.gchip--card .gchip__flag{
  position:absolute; top:13px; right:13px;
  width:24px; height:24px; border-radius:999px;
  display:grid; place-items:center;
  background:var(--surface); border:1px solid var(--line); box-shadow:var(--sh-1);
}

/* ==========================================================================
   NARROW — the result as a bottom sheet

   A phone has no columns for a result to live in. The same markup that is
   three columns on a desktop stacks here, and a result that begins below a
   hero band, a search box and a category rail is a result you have to go
   looking for.

   So .ws__body itself becomes the sheet. Nothing is re-rendered to do it: on a
   phone that element already holds exactly the right things in exactly the
   right order — the categories column is display:none, the centre column has
   the sticky master header and the brand-wise device list, and the right
   column, which is display:block down here, has the selected device. Making
   that one box fixed, rounded and scrollable turns the page into a sheet
   without moving a node, so every id, handler and repaint keeps working.
   ========================================================================== */
@media (max-width:1179px){

  .gscrim{
    position:fixed; inset:0; z-index:90; background:rgba(6,20,18,.5);
    backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px);
    animation:fade .2s ease;
  }

  .ws__body--sheet{
    position:fixed; z-index:100; left:0; right:0; bottom:0;
    /* Everything below the category selector, which floats in the band above
       the sheet: that band was a blurred strip of the finder showing through,
       and it is now the one control that decides which part is on screen. */
    max-height:calc(100dvh - var(--gcats-h)); height:calc(100dvh - var(--gcats-h));
    display:flex; flex-direction:column;
    background:var(--surface);
    border-radius:var(--r-xl) var(--r-xl) 0 0;
    box-shadow:var(--sh-3);
    overflow-y:auto; overscroll-behavior:contain;
    -webkit-overflow-scrolling:touch;
    /* The columns' own padding is gone with the columns (below), so the sheet
       carries it. padding-inline on the scroller, not on the flattened
       children, is what lets the sticky header bleed to both edges. */
    padding:0 14px calc(22px + env(safe-area-inset-bottom));
    animation:gsheetup .32s cubic-bezier(.2,.9,.25,1);
  }
  /* By offset, not by transform. A transformed ancestor becomes the
     containing block of everything position:fixed inside it, and the
     category selector is fixed above the sheet — a transform here would hold
     it inside the sheet for the length of the animation and then let it jump
     to the top of the screen. */
  @keyframes gsheetup{ from{ bottom:-14%; opacity:.4; } }
  @media (prefers-reduced-motion:reduce){
    .ws__body--sheet{ animation:none; }
  }

  /* The page behind the sheet must not scroll with it. */
  body.has-gsheet{ overflow:hidden; }

  /* The columns are flattened away inside the sheet.

     position:sticky cannot leave its own containing block, and the device
     toolbar lives several boxes deep. Flattened, every part of the open group
     is a direct item of the sheet's own column, so the toolbar sticks for the
     whole scroll. */
  .ws__body--sheet .ws__center,
  .ws__body--sheet .ws__center > .ws__head{ display:contents; }

  .ws__body--sheet .gbrand__h{ background:var(--surface); }
}

@media (max-width:560px){
  .gchips{ grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); }
  .gchip{ padding:4px 8px 4px 4px; gap:8px; }
  .gchip__ph{ width:26px; height:26px; }
  .gchip__n{ font-size:12.5px; }

  /* Two across on any phone: a 132px floor fits two cards in the 292px a
     320px screen leaves inside the sheet. */
  .gchips.gchips--cards{ --gc-min:132px; --gc-gap:10px; }
  .gchip--card .gchip__ph{ height:92px; }
  .gchip.gchip--card{ padding:7px 7px 10px; gap:8px; }
}

/* ============================================================================
   Model detail page — specification database layout (.dev-page--rich)
   ----------------------------------------------------------------------------
   The All Mobile Models detail view for a migrated model. Reference points are
   the information architecture of the established spec databases: a compact
   hero, an overview strip, a variant picker, then plain label/value tables.
   None of their design, markup or assets is used.

   Nothing here is loaded by the Compatibility Device Finder.

   THEMES. The page carries its own token set so the sheet reads as one
   document, and redeclares it for dark mode — both the explicit toggle
   (data-theme="dark") and the system preference when no explicit choice is
   stored. The hero gradient is defined once per theme as --hero-grad.

   THE IMAGE PANEL STAYS LIGHT IN BOTH THEMES. Catalogue photographs are JPEGs
   with a white background baked in; putting one on a dark surface draws a
   white rectangle around the handset. The gradient never runs under the
   photograph for the same reason — it would tint the product.

   ONE SCROLL CONTAINER. No section here creates a second one. A nested
   overflow box swallows the wheel when the pointer is over it, which is what
   made the earlier sheet feel like it stuck.
   ========================================================================== */

.dev-page--rich{
  --paper:#FFFFFF; --paper-2:#F7F9F9;
  --surface:#FFFFFF; --surface-2:#F7F9F9; --surface-3:#EEF2F2;
  --line:#E3E8E8; --line-soft:#EEF2F2;
  --ink:#101A1A; --ink-1:#101A1A; --ink-2:#48595A; --ink-3:#6B7C7D;
  --teal-700:#0F766E; --teal-600:#0F766E; --teal-050:#F0FAF7;
  --hero-grad:linear-gradient(135deg,#F3FAF8 0%,#EDF6F3 42%,#E9F2F6 100%);
  --hero-line:#DCE9E6;
  --hero-chip:rgba(255,255,255,.72); --hero-count:rgba(16,26,26,.06);
  background:var(--paper); color:var(--ink-1);
}
:root[data-theme="dark"] .dev-page--rich{
  --paper:#0A1413; --paper-2:#0E1B19;
  --surface:#0E1B19; --surface-2:#122320; --surface-3:#172C29;
  --line:#1F3733; --line-soft:#182C29;
  --ink:#E8F2EF; --ink-1:#E8F2EF; --ink-2:#A9C0BB; --ink-3:#88A29D;
  --teal-700:#2BD3AE; --teal-600:#2BD3AE; --teal-050:#0F2A26;
  --hero-grad:linear-gradient(135deg,#13322D 0%,#102523 45%,#0F1F27 100%);
  --hero-line:#22423C;
  --hero-chip:rgba(255,255,255,.05); --hero-count:rgba(255,255,255,.09);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .dev-page--rich{
    --paper:#0A1413; --paper-2:#0E1B19;
    --surface:#0E1B19; --surface-2:#122320; --surface-3:#172C29;
    --line:#1F3733; --line-soft:#182C29;
    --ink:#E8F2EF; --ink-1:#E8F2EF; --ink-2:#A9C0BB; --ink-3:#88A29D;
    --teal-700:#2BD3AE; --teal-600:#2BD3AE; --teal-050:#0F2A26;
    --hero-grad:linear-gradient(135deg,#13322D 0%,#102523 45%,#0F1F27 100%);
    --hero-line:#22423C;
    --hero-chip:rgba(255,255,255,.05); --hero-count:rgba(255,255,255,.09);
  }
}

.dev-page--rich .wrap.rich{ max-width:1140px; margin:0 auto; padding:0 20px 60px; }
.dev-page--rich .dhead{ background:var(--surface); border-bottom:1px solid var(--line); }
.dev-page--rich .dhead__t{ color:var(--ink-1); }

/* Ad slots: reserved, empty and zero-height until something is placed in them,
   so adding a network later cannot reflow the sheet under the reader. */
.adslot{ margin:16px 0; min-height:0; }
.adslot:not(:empty){
  min-height:90px; display:flex; align-items:center; justify-content:center;
  border:1px dashed var(--line); border-radius:14px; background:var(--surface-2);
}

/* --------------------------------------------------------------------- hero */
.hero{
  display:grid; grid-template-columns:300px minmax(0,1fr); gap:18px;
  align-items:stretch; margin:22px 0 4px;
}
/* Deliberately light in both themes — see the file header. */
.hero__media{
  background:#FFFFFF; border:1px solid var(--line); border-radius:18px;
  padding:22px; display:flex; align-items:center; justify-content:center;
  aspect-ratio:4 / 5;
}
.hero__media img{ max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain; display:block; }

.hero__panel{
  background:var(--hero-grad); border:1px solid var(--hero-line);
  border-radius:18px; padding:26px 28px; min-width:0;
  display:flex; flex-direction:column;
}
/* Brand mark leads, name sits to its right. The mark is given real size —
   it is the fastest way to recognise whose page this is — but is trimmed back
   from the site's --lg default so it does not outweigh the model name. */
.hero__brand{ display:flex; align-items:center; gap:12px; margin-bottom:12px; }
.hero__brand .blogo--lg{ width:46px; height:42px; border-radius:11px; font-size:15px; flex:none; }
.hero__brand .blogo--lg.blogo--img,
.hero__brand .blogo--lg.blogo--mark,
.hero__brand .blogo--lg.blogo--word{ padding:6px; }
.hero__brand span{
  font-size:14px; font-weight:700; letter-spacing:.05em;
  text-transform:uppercase; color:var(--ink-2);
}
.hero__name{
  font-family:var(--f-display); font-weight:800; letter-spacing:-.022em;
  font-size:clamp(26px,3.1vw,36px); line-height:1.08; margin:0 0 10px; color:var(--ink-1);
}
.hero__rel{ margin:0 0 18px; font-size:13px; color:var(--ink-2); }
.hero__rel span{
  font-size:10.5px; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
  color:var(--ink-3); margin-right:8px;
}

/* ------------------------------------------------- headline specifications
   Three across, two rows: five fixed specs plus one dynamic extra. No border
   per cell — spacing separates them, so the block reads as one system rather
   than six cards. Every cell is icon / title / value / qualifier in the same
   order, so the eye lands in the same place in all six. */
.hspecs{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  gap:18px 20px; margin:0 0 22px;
  padding:18px 0; border-top:1px solid var(--hero-line); border-bottom:1px solid var(--hero-line);
}
.hspec{ display:flex; gap:11px; align-items:flex-start; min-width:0; }
.hspec__i{
  flex:none; display:flex; align-items:center; justify-content:center;
  width:34px; height:34px; border-radius:10px;
  background:var(--hero-chip); border:1px solid var(--hero-line);
}
.hspec__i svg{ width:17px; height:17px; color:var(--teal-700); }
.hspec__b{ min-width:0; display:block; padding-top:1px; }
.hspec__l{
  display:block; font-size:9.5px; font-weight:700; letter-spacing:.09em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:3px;
}
.hspec__v{
  display:block; font-size:15px; font-weight:750; line-height:1.25;
  color:var(--ink-1); overflow-wrap:anywhere;
}
.hspec__s{
  display:block; font-style:normal; font-size:11.5px; line-height:1.35;
  color:var(--ink-3); margin-top:2px; overflow-wrap:anywhere;
}

/* --------------------------------------------------------- actions row
   One row: the primary action, then the categories this model actually has
   parts in. The icon is the control; the badge is how many handsets that
   compatibility group covers. */
.hero__cta{ display:flex; align-items:center; gap:14px; flex-wrap:wrap; margin-top:auto; }
.hcats{ display:flex; gap:9px; flex-wrap:wrap; }
.hcat{
  position:relative; appearance:none; cursor:pointer; font:inherit;
  width:46px; height:46px; padding:6px; border-radius:12px;
  border:1px solid var(--hero-line); background:var(--hero-chip);
  display:flex; align-items:center; justify-content:center;
  transition:border-color .12s ease, background .12s ease, transform .12s ease;
}
.hcat:hover{ border-color:var(--teal-700); background:var(--teal-050); transform:translateY(-1px); }
.hcat:focus-visible{ outline:2px solid var(--teal-700); outline-offset:2px; }
.hcat__art{ width:100%; height:100%; object-fit:contain; display:block; }
.hcat__art img{ width:100%; height:100%; object-fit:contain; }
/* Floating count, notification style. */
.hcat__n{
  position:absolute; top:-6px; right:-6px; min-width:19px; height:19px;
  padding:0 5px; border-radius:99px;
  background:var(--teal-700); color:#fff;
  font-size:10.5px; font-weight:800; line-height:19px; text-align:center;
  box-shadow:0 0 0 2px var(--hero-grad-solid, var(--surface));
}
:root[data-theme="dark"] .hcat__n{ color:#08201C; }

@media (max-width:1000px){
  .hspecs{ gap:16px; }
  .hspec__v{ font-size:14px; }
}
@media (max-width:820px){
  .hspecs{ grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media (max-width:560px){
  .hero__brand .blogo--lg{ width:40px; height:36px; }
  .hspecs{ grid-template-columns:minmax(0,1fr); gap:14px; padding:15px 0; }
  .hero__cta .btn{ width:100%; justify-content:center; }
  .hcats{ width:100%; }
}

/* ---------------------------------------------------------- variant picker */
.vsel{
  margin:18px 0 0; padding:20px 22px 22px;
  border:1px solid var(--line); border-radius:16px; background:var(--surface);
}
.vsel__h{ display:flex; align-items:baseline; gap:11px; flex-wrap:wrap; margin-bottom:16px; }
.vsel__h h2{ font-family:var(--f-display); font-size:17px; margin:0; color:var(--ink-1); }
.vsel__n{ font-size:12px; color:var(--ink-3); }

/* Preview beside the controls: the choice and its consequence as one object. */
.vsel__body{ display:grid; grid-template-columns:168px minmax(0,1fr); gap:22px; align-items:start; }
.vsel__prev{
  background:#FFFFFF; border:1px solid var(--line); border-radius:12px;
  padding:12px; display:flex; flex-direction:column; align-items:center; gap:8px;
}
.vsel__prev img{ max-width:100%; height:auto; object-fit:contain; display:block; }
.vsel__prevl{ font-size:11.5px; font-weight:600; color:#48595A; text-align:center; }
.vsel__ctl{ min-width:0; }

.vrow{ display:grid; grid-template-columns:76px minmax(0,1fr); gap:14px; align-items:start; margin-bottom:12px; }
.vrow:last-child{ margin-bottom:0; }
.vrow__l{
  font-size:10.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase;
  color:var(--ink-3); padding-top:9px;
}
.vrow__o{ display:flex; gap:8px; flex-wrap:wrap; }

.vopt{
  appearance:none; cursor:pointer; font:inherit;
  padding:8px 14px; border-radius:10px; border:1px solid var(--line);
  background:var(--surface); color:var(--ink-1);
  font-size:13.5px; font-weight:600; line-height:1.2;
  transition:border-color .12s ease, background .12s ease, color .12s ease;
}
.vopt:hover:not(.is-off):not(.is-on){ border-color:var(--teal-700); background:var(--teal-050); }
.vopt.is-on{ background:var(--teal-700); border-color:var(--teal-700); color:#FFFFFF; }
:root[data-theme="dark"] .vopt.is-on{ color:#08201C; }
/* Never sold in this market: still listed, so the sheet says what exists. */
.vopt.is-off{ opacity:.4; cursor:not-allowed; text-decoration:line-through; }
.vopt:focus-visible{ outline:2px solid var(--teal-700); outline-offset:2px; }

/* Derived, read-only result of the selection. The model number lives here
   rather than in the picker because it is never chosen — it follows market. */
.vout{
  display:grid; gap:16px 28px; grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
  margin-top:18px; padding:16px 18px; border-radius:12px;
  background:var(--surface-2); border:1px solid var(--line-soft);
}
.vout__l{
  display:block; font-size:10.5px; font-weight:700; letter-spacing:.07em;
  text-transform:uppercase; color:var(--ink-3); margin-bottom:4px;
}
.vout__cell b{ font-size:16px; font-weight:750; color:var(--ink-1); }
.vout__mn{ font-family:var(--f-mono,ui-monospace,monospace); font-size:18px !important; }
.vout__hint{ display:block; font-style:normal; font-size:11px; color:var(--ink-3); margin-top:3px; }
.vout__p{ color:var(--teal-700) !important; font-size:20px !important; }
.vout__na{ color:var(--ink-3) !important; font-size:15px !important; font-weight:600 !important; }
.vsel__note{ margin:12px 0 0; font-size:12.5px; line-height:1.55; color:var(--ink-3); }
.vsel__note b{ color:var(--ink-2); font-weight:600; }

/* ---------------------------------------------------------- specification

   NAMESPACED (mspec/mrow). The plain names collide with an existing `.spec`
   component in this same file that is display:flex and styles its own dt/dd —
   which put every section header beside its rows and quietly overrode the type
   scale here. Namespacing removes the clash instead of fighting specificity.

   PACKED BY COLUMNS, NOT GRID. A grid row stretches every card to the tallest
   in it, which is what left a large hole beside Camera. CSS columns let each
   card take exactly its content height and pull the next card up into the
   space, so there are no invented gaps and no equal-height cards. Camera is
   lifted out as its own full-width block because it carries far more rows than
   anything that could sit beside it.

   ONE VALUE COLUMN. The label track is a fixed width and the value is right
   aligned, so values form a straight edge down the card no matter how long the
   labels are — the thing that makes a spec sheet scannable.                  */

.sheet-specs{ margin-top:20px; }

.mdsp-grid{
  columns:2; column-gap:16px;
  margin:0 0 16px;
}
.mdsp-sec{
  break-inside:avoid; -webkit-column-break-inside:avoid; page-break-inside:avoid;
  display:inline-block; width:100%;      /* keeps a card whole inside a column */
  border:1px solid var(--line); border-radius:14px; background:var(--surface);
  margin:0 0 16px; overflow:hidden;
}
.mdsp-sec--wide{ display:block; width:auto; }

.mdsp-sec__h{
  display:flex; align-items:center; justify-content:center; gap:9px; margin:0;
  padding:11px 18px;
  font-family:var(--f-display); font-size:12.5px; font-weight:800;
  letter-spacing:.11em; text-transform:uppercase; color:var(--ink-2);
  background:var(--surface-2); border-bottom:1px solid var(--line);
}
.mdsp-sec__h svg{ width:15px; height:15px; flex:none; color:var(--teal-700); }

.mdsp-sec__b{ margin:0; padding:2px 0; }

.mdsp-row{
  /* Fixed label track, not a percentage: the value column then starts at the
     same x in every card, so the values form a straight left edge down the
     page. Right-aligning them instead lines up their ENDS, which puts a
     ragged edge exactly where the eye starts reading. */
  display:grid; grid-template-columns:170px minmax(0,1fr); gap:14px;
  padding:9px 18px; align-items:baseline;
}
.mdsp-row + .mdsp-row{ border-top:1px solid var(--line-soft); }
.mdsp-row__l{ font-size:13px; font-weight:500; color:var(--ink-3); line-height:1.45; }
.mdsp-row__v{
  margin:0; font-size:14px; font-weight:650; color:var(--ink-1);
  line-height:1.45; text-align:left; overflow-wrap:anywhere;
}

/* Camera runs full width, so its rows split into two tracks rather than
   stretching a single label/value pair across the whole page. */
.mdsp-sec--wide .mdsp-sec__b{
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  column-gap:0; padding:2px 0;
}
.mdsp-sec--wide .mdsp-row{ border-top:1px solid var(--line-soft); }
.mdsp-sec--wide .mdsp-row:nth-child(-n+2){ border-top:0; }
.mdsp-sec--wide .mdsp-row:nth-child(odd){ border-right:1px solid var(--line-soft); }

@media (max-width:1000px){
  .mdsp-row{ grid-template-columns:148px minmax(0,1fr); gap:12px; padding:9px 15px; }
  .mdsp-sec--wide .mdsp-sec__b{ grid-template-columns:minmax(0,1fr); }
  .mdsp-sec--wide .mdsp-row:nth-child(odd){ border-right:0; }
  .mdsp-sec--wide .mdsp-row:nth-child(2){ border-top:1px solid var(--line-soft); }
}
@media (max-width:760px){
  .mdsp-grid{ columns:1; }
  .mdsp-sec{ margin-bottom:14px; }
  .mdsp-row{ grid-template-columns:minmax(0,1fr); gap:2px; padding:10px 15px; }
  /* Label above value on a narrow screen; the value returns to the left so it
     stays beside its own label rather than floating away from it. */
  .mdsp-row__v{ text-align:left; }
  .mdsp-row__l{ font-size:12.5px; }
  .mdsp-sec__h{ padding:11px 15px; }
}

/* --------------------------------------------------------------- provenance */
.prov{
  margin:6px 0 0; padding:18px 22px; border:1px solid var(--line);
  border-radius:16px; background:var(--surface-2);
}
.prov__b{ display:grid; gap:14px 30px; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); margin:0; }
.prov__b dt{
  font-size:10.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase;
  color:var(--ink-3); margin-bottom:3px;
}
.prov__b dd{ margin:0; font-size:13.5px; font-weight:600; color:var(--ink-1); }
.prov__l{ margin:14px 0 0; font-size:12.5px; color:var(--ink-3); }
.prov__l a{ color:var(--teal-700); }
.prov__d{ margin-top:12px; font-size:12.5px; }
.prov__d summary{ cursor:pointer; font-weight:600; color:var(--ink-2); }
.prov__d ul{ margin:10px 0 0; padding-left:17px; color:var(--ink-3); }
.prov__d li{ margin-bottom:8px; line-height:1.55; }

/* ------------------------------------------------------------ related models */
.rel{ margin-top:28px; }
.rel__h{ font-family:var(--f-display); font-size:19px; margin:0 0 14px; color:var(--ink-1); }
.rel__g{ display:grid; gap:12px; grid-template-columns:repeat(auto-fill,minmax(160px,1fr)); }
.rel__c{
  display:flex; flex-direction:column; align-items:flex-start; gap:4px;
  padding:14px 15px 13px; border:1px solid var(--line); border-radius:14px;
  background:var(--surface); text-decoration:none; color:inherit; min-width:0;
  transition:border-color .12s ease, box-shadow .12s ease;
}
.rel__c:hover{ border-color:var(--teal-700); box-shadow:0 2px 10px -6px rgba(15,118,110,.4); }
.rel__s{
  display:flex; align-items:center; justify-content:center; height:100px; width:100%;
  margin-bottom:6px; background:#FFFFFF; border-radius:9px;
}
.rel__s img{ max-height:92px; width:auto; object-fit:contain; }
.rel__n{ font-size:13.5px; font-weight:700; color:var(--ink-1); line-height:1.35; overflow-wrap:anywhere; }
.rel__d{ font-style:normal; font-size:12px; color:var(--ink-3); }
.rel__v{ display:inline-flex; align-items:center; gap:4px; margin-top:6px; font-size:12px; font-weight:600; color:var(--teal-700); }
.rel__v svg{ width:13px; height:13px; }

.dev-page--rich .dcompat{ margin-top:30px; }
.dev-page--rich .dcompat h2{ color:var(--ink-1); }

/* --------------------------------------------------------------- responsive */
@media (max-width:1000px){
  .hero{ grid-template-columns:240px minmax(0,1fr); gap:14px; }
  .hero__panel{ padding:22px; }
  /* One column before the cards get too narrow to hold a label and a value
     on the same line. */
}
@media (max-width:760px){
  .dev-page--rich .wrap.rich{ padding:0 16px 46px; }
  /* Stacked, and the image panel is capped so it cannot become a tall band
     that pushes the model name below the fold. */
  .hero{ grid-template-columns:minmax(0,1fr); gap:12px; margin-top:16px; }
  .hero__media{ aspect-ratio:auto; padding:16px; max-height:280px; }
  .hero__media img{ max-height:248px; }
  .hero__panel{ padding:20px 18px; }
  .hero__facts{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:13px 16px; }
  .hero__cta .btn{ width:100%; justify-content:center; }
  .hero__cta{ gap:10px; }
  .vsel{ padding:17px 16px 19px; }
  .vsel__body{ grid-template-columns:minmax(0,1fr); gap:16px; }
  .vsel__prev{ flex-direction:row; justify-content:center; gap:14px; }
  .vsel__prev img{ max-height:120px; }
  /* Label above value on a narrow screen: a fixed label column would leave the
     values a few characters wide. */
  .vrow{ grid-template-columns:minmax(0,1fr); gap:8px; }
  .vrow__l{ padding-top:0; }
  .vout{ grid-template-columns:minmax(0,1fr); gap:14px; }
  /* Label above value on a narrow screen; the value returns to the left so it
     does not float away from its own label. */
  .prov{ padding:16px; }
  .rel__g{ grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media (max-width:420px){
}

/* ==========================================================================
   COMPATIBLE MODELS — compact chips, grid cards, specification table
   ----------------------------------------------------------------------------
   One look for a list of compatible models wherever it appears: the result
   page, the opened group and the home feed's previews. Every colour is a theme
   token, so light and dark follow the same rules; the only fixed white is the
   ground the catalogue's own photographs were shot on (--ctile-paper).
   ========================================================================== */

/* ---- compact: the full name in a thin outline, as wide as the name -------- */
.mchips{ display:flex; flex-wrap:wrap; gap:6px; align-items:flex-start; }
.mchip{
  display:inline-flex; align-items:center; gap:5px;
  max-width:100%; min-height:30px; padding:4px 10px;
  background:transparent; color:var(--ink);
  border:1px solid var(--line); border-radius:7px;
  font:inherit; font-size:13px; font-weight:600; line-height:1.3; text-align:left;
  cursor:pointer; transition:border-color .12s, background .12s, color .12s;
}
/* A long name wraps INSIDE its own outline rather than being cut. */
.mchip__n{ min-width:0; overflow-wrap:anywhere; }
button.mchip:hover{ border-color:var(--teal-500); background:var(--surface-2); }
.mchip:focus-visible{ outline:2px solid var(--teal-500); outline-offset:2px; }
.mchip.is-on{ border-color:var(--teal-500); background:var(--teal-050); color:var(--teal-700); }
.mchip.is-master{ border-color:var(--amber-400); }
.mchip.is-hit{ border-color:var(--teal-500); }
.mchip__flag{ flex:none; display:inline-flex; color:var(--amber-700); }
.mchip__flag--hit{ color:var(--teal-600); }
.mchip__flag svg{ width:12px; height:12px; }
/* In a home-feed card the chip is a label, not a control: the card is the
   control. Same outline, no pointer of its own. */
.mchip--static{ cursor:inherit; min-height:26px; padding:3px 8px; font-size:12px; }
.mchip--more{
  min-height:26px; padding:3px 8px; font-size:12px; font-weight:700;
  color:var(--ink-3); border-style:dashed; cursor:inherit;
}
.mchip--more svg{ width:11px; height:11px; }

/* ---- the home feed's preview, inside a group card ------------------------- */
.pchips{ display:flex; flex-wrap:wrap; gap:5px; }
/* THREE ROWS, ALWAYS. The name box is a fixed three chip-rows tall, so every
   card is the same height whatever its group holds; C.fitPreviews hides the
   names that do not fit and counts them into "+N more". A card drawn before
   its names arrive holds the same box, so nothing moves when they land. */
.pchips{
  height:calc(3 * 26px + 2 * 5px); overflow:hidden;
  align-content:flex-start;
}
.mchip[hidden]{ display:none; }

/* ---- the view switch: Compact / Grid / Table ------------------------------ */
.lview{
  display:inline-flex; gap:2px; padding:2px; margin-left:auto;
  background:var(--surface-3); border:1px solid var(--line); border-radius:9px;
}
.lview__b{
  display:inline-flex; align-items:center; gap:5px; height:30px; padding:0 10px;
  border:0; border-radius:7px; background:transparent; color:var(--ink-2);
  font:inherit; font-size:12.5px; font-weight:700; cursor:pointer;
}
.lview__b svg{ width:15px; height:15px; }
.lview__b:hover{ color:var(--ink); }
.lview__b.is-on{ background:var(--surface); color:var(--teal-700); box-shadow:var(--sh-1); }
.lview__b:focus-visible{ outline:2px solid var(--teal-500); outline-offset:1px; }
@media (max-width:420px){
  .lview__b{ padding:0 8px; }
  .lview__t{ display:none; }
}

/* ---- table: every specification, scrolled sideways rather than cut -------- */
.stable-wrap{
  width:100%; overflow-x:auto; -webkit-overflow-scrolling:touch;
  border:1px solid var(--line); border-radius:var(--r);
  background:var(--surface);
  scrollbar-width:thin; scrollbar-color:var(--line) transparent;
}
.stable-wrap:focus-visible{ outline:2px solid var(--teal-500); outline-offset:2px; }
.stable{
  width:100%; min-width:620px; border-collapse:separate; border-spacing:0;
  font-size:13px; font-variant-numeric:tabular-nums;
}
.stable th, .stable td{
  padding:8px 12px; text-align:right; white-space:nowrap;
  border-bottom:1px solid var(--line-soft); background:var(--surface); color:var(--ink);
}
.stable thead th{
  position:sticky; top:0; z-index:2;
  background:var(--surface-2); color:var(--ink-2);
  font-size:11px; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
}
.stable__u{ font-weight:600; letter-spacing:0; text-transform:none; color:var(--ink-3); }
.stable tbody tr:last-child th, .stable tbody tr:last-child td{ border-bottom:0; }
/* The name column stays where it is while the numbers scroll under it. */
.stable .stable__dev{
  position:sticky; left:0; z-index:1; text-align:left;
  min-width:190px; max-width:260px; white-space:normal;
  box-shadow:1px 0 0 var(--line-soft);
}
.stable thead .stable__dev{ z-index:3; }
.stable__devin{ display:flex; align-items:center; gap:9px; }
.stable__n{ min-width:0; font-weight:700; overflow-wrap:anywhere; }
.stable__ph{
  position:relative; flex:none; width:24px; height:30px;
  display:flex; align-items:center; justify-content:center;
  background:var(--ctile-paper); border-radius:5px; overflow:hidden;
}
.stable__img{ max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain; display:block; }
.stable__ph.is-failed .stable__img{ display:none; }
.stable__ph.is-failed .gchip__fb{ display:block; width:11px; height:17px; }
.stable__flag{ flex:none; display:inline-flex; color:var(--amber-700); }
.stable__flag--hit{ color:var(--teal-600); }
.stable__flag svg{ width:12px; height:12px; }
.stable__none{ color:var(--ink-3); }
.stable tbody tr{ cursor:pointer; }
.stable tbody tr:hover th, .stable tbody tr:hover td{ background:var(--surface-2); }
.stable tbody tr:focus-visible{ outline:2px solid var(--teal-500); outline-offset:-2px; }
.stable tbody tr.is-on th, .stable tbody tr.is-on td{ background:var(--teal-050); }
.stable tbody tr.is-on .stable__n{ color:var(--teal-700); }

/* ---- grid: three across on a phone, more as the column widens ------------- */
@media (max-width:560px){
  .gchips.gchips--cards{ --gc-min:96px; --gc-gap:8px; }
  .gchip--card .gchip__ph{ height:78px; padding:5px; }
  .gchip.gchip--card{ padding:6px 5px 8px; gap:6px; }
  .gchip--card .gchip__n{ font-size:12px; padding:0 2px; }
  .gchip--card .gchip__flag{ top:9px; right:9px; width:20px; height:20px; }
}

/* "13 more — part of a plan", under a free preview. */
.lockrow{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin-top:12px; padding:10px 12px;
  background:var(--amber-050); border:1px solid var(--amber-100); border-radius:var(--r-sm);
  color:var(--ink-2); font-size:13px;
}
.lockrow__ico{ display:inline-flex; color:var(--amber-700); }
.lockrow__ico svg{ width:16px; height:16px; }
.lockrow__t{ flex:1 1 200px; min-width:0; }
.lockrow__t b{ color:var(--ink); }


@media (prefers-reduced-motion:reduce){
  .mchip, .lview__b{ transition:none; }
}

/* ==========================================================================
   THE OPEN GROUP — category selector, master header, compatible devices
   ----------------------------------------------------------------------------
   One layout for a searched model and an opened group. Colours are theme
   tokens; the only fixed white is --ctile-paper, the ground the catalogue's
   photographs were shot on, so a part never sits in a grey box in either
   theme.
   ========================================================================== */
:root{ --gcats-h:100px; }

/* ---- the category selector -------------------------------------------- */
.gcats{ position:relative; z-index:8; }
.gcats__row{
  display:flex; gap:8px; align-items:stretch;
  overflow-x:auto; overflow-y:hidden; scrollbar-width:none;
  padding:10px 2px;
  -webkit-overflow-scrolling:touch; scroll-snap-type:x proximity;
}
.gcats__row::-webkit-scrollbar{ display:none; }
.gcat{
  flex:none; width:76px; scroll-snap-align:start;
  display:flex; flex-direction:column; align-items:center; gap:4px;
  padding:6px 5px 6px; cursor:pointer;
  background:var(--surface); color:var(--ink-2);
  border:1px solid var(--line); border-radius:12px;
  box-shadow:0 2px 8px -3px rgba(10,21,18,.22);
  font:inherit; transition:border-color .14s, box-shadow .14s, color .14s;
}
.gcat:hover{ border-color:var(--teal-500); }
.gcat:focus-visible{ outline:2px solid var(--teal-500); outline-offset:2px; }
.gcat__art{ display:block; width:44px; height:44px; }
.pthumb.pthumb--gcat{
  width:100%; height:100%; border:0; box-shadow:none; border-radius:8px;
  background:var(--ctile-paper);
}
.gcat__n{
  font-size:10.5px; font-weight:800; line-height:1.15; letter-spacing:-.01em;
  text-align:center; white-space:normal; overflow-wrap:anywhere;
}
.gcat.is-on{
  color:var(--teal-700); border-color:var(--teal-500);
  box-shadow:0 0 0 2px var(--teal-500), 0 6px 14px -6px rgba(15,118,110,.55);
}
.gcat.is-off{ opacity:.42; cursor:default; box-shadow:none; }

/* ---- the master model's header ----------------------------------------- */
.mhead{
  flex:none; position:relative;
  background:var(--surface); color:var(--ink);
  border:1px solid var(--line); border-radius:var(--r-lg);
  padding:12px 14px 14px; margin:12px 0 0;
}
.sheet-grab{ display:none; }
.mhead__top{ display:flex; align-items:center; gap:12px; }
.mhead__shot, .mhead__part{
  flex:none; width:68px; height:88px; border-radius:var(--r-sm); overflow:hidden;
  display:flex; align-items:center; justify-content:center;
  background:var(--ctile-paper);
}
.mhead__shot{ padding:4px; }
.mhead__shot .dphoto{ width:100%; height:100%; }
.mhead__shot .dphoto__img,
.mhead__shot .dphoto__art{ max-width:100%; max-height:100%; width:auto; height:auto; object-fit:contain; }
/* The part, bare: its own white is its ground, no tile and no inner line.
   Fitted to the part rather than to its square canvas by the per-asset focus
   numbers, so it fills the box without being stretched. */
.pthumb.pthumb--mhead{
  position:relative; width:100%; height:100%; padding:0;
  border:0; border-radius:var(--r-sm); box-shadow:none; background:var(--ctile-paper);
}
.pthumb--mhead img.pimg{
  position:absolute; width:var(--iw,100%); height:auto; max-width:none;
  left:var(--il,0); top:var(--it,0); object-fit:contain;
}
.pthumb--mhead svg.pimg{ width:100%; height:100%; padding:10%; }
.mhead__id{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:3px; }
.mhead__eyebrow{ display:flex; flex-wrap:wrap; align-items:center; gap:4px 8px; }
.mhead__eyebrow .masterflag{ margin-bottom:0; }
.mhead__cat{
  font-size:10.5px; font-weight:800; letter-spacing:.07em; text-transform:uppercase;
  color:var(--teal-700);
}
.mhead__n{
  margin:0; font-family:var(--f-display); font-weight:800; font-size:22px;
  line-height:1.1; letter-spacing:-.025em; color:var(--ink); overflow-wrap:anywhere;
}
.mhead__hit{
  display:inline-flex; align-items:center; gap:5px; align-self:flex-start;
  font-size:12px; font-weight:700; color:var(--teal-700);
}
.mhead__hit svg{ width:13px; height:13px; }

.mhead__codes{ display:flex; flex-wrap:wrap; gap:4px 16px; margin:12px 0 0; }
.mhead__codes div{ display:flex; align-items:baseline; gap:6px; min-width:0; }
.mhead__codes dt, .mhead__specs dt{
  font-size:10px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3);
}
.mhead__codes dd{ margin:0; font-family:var(--f-mono); font-size:12.5px; font-weight:600; color:var(--ink); }

/* The master's measurements — the same five the table shows, in the same
   units, so the header and the table can be read against each other. */
.mhead__specs{
  display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:1px;
  margin:12px 0 0; overflow:hidden;
  background:var(--line-soft); border:1px solid var(--line); border-radius:var(--r-sm);
}
.mhead__specs div{ background:var(--surface-2); padding:7px 10px; min-width:0; }
.mhead__specs dt span{ font-weight:600; letter-spacing:0; text-transform:none; }
.mhead__specs dd{
  margin:2px 0 0; font-size:15px; font-weight:800; color:var(--ink);
  font-variant-numeric:tabular-nums;
}

/* ---- the compatible devices -------------------------------------------- */
.gopen + .gopen{ margin-top:22px; }
.gdev{ flex:none; display:block; }
.gdev__bar{
  position:sticky; top:0; z-index:5; flex:none;
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  margin:12px 0 0; padding:10px 0;
  background:var(--paper); border-bottom:1px solid var(--line-soft);
}
.gdev__t{
  font-size:11px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-2);
}
.gdev__list{ flex:none; padding:12px 0 8px; }

/* ---- focused layout: the centre column alone ---------------------------- */
@media (min-width:1180px){
  .ws__body.ws__body--focus{ grid-template-columns:minmax(0,1fr); }
  .ws__body--focus .ws__left,
  .ws__body--focus .ws__right{ display:none; }
  .ws__body--focus .ws__center{ padding-inline:max(24px, calc((100% - 1040px) / 2)); }
  /* The selector stays at the top of the column; the device toolbar sticks
     under it, so both remain while a long list scrolls. */
  .ws__body--focus .gcats{
    position:sticky; top:0; height:var(--gcats-h);
    background:var(--paper); display:flex; align-items:center;
  }
  .ws__body--focus .gdev__bar{ top:var(--gcats-h); }
}

/* ---- the phone sheet --------------------------------------------------- */
@media (max-width:1179px){
  /* Floating in the band above the sheet, over the dimmed finder. Fixed, so
     it stays while the sheet scrolls; inside the sheet's own layer, so it is
     above the scrim and goes when the sheet does. */
  .ws__body--sheet .gcats{
    position:fixed; left:0; right:0; top:0; height:var(--gcats-h);
    padding:0 12px; display:flex; align-items:center;
  }
  .ws__body--sheet .gcats__row{ padding:8px 2px; }
  /* The header is the top of the white panel: square to the sheet's edges,
     with the grab handle, and no card border of its own. */
  .ws__body--sheet .mhead{
    margin:0 -14px; border:0; border-radius:var(--r-xl) var(--r-xl) 0 0;
    border-bottom:1px solid var(--line-soft); padding:4px 14px 14px;
  }
  .ws__body--sheet .sheet-grab{
    display:block; width:38px; height:4px; border-radius:99px;
    background:var(--line); margin:4px auto 8px;
  }
  .ws__body--sheet .gdev__bar{ background:var(--surface); margin-top:0; }
  .ws__body--sheet .gopen + .gopen .mhead{ border-radius:0; margin-top:18px; border-top:1px solid var(--line); }
}
@media (max-width:560px){
  .mhead__top{ gap:10px; }
  .mhead__shot, .mhead__part{ width:56px; height:72px; }
  .mhead__n{ font-size:18.5px; }
  .mhead__codes{ gap:3px 12px; }
  .mhead__specs{ grid-template-columns:repeat(3,minmax(0,1fr)); }
  /* Five measurements in three columns: the last one takes the empty cell
     beside it rather than leaving a grey hole in the grid. */
  .mhead__specs div:last-child:nth-child(3n+2){ grid-column:span 2; }
  .mhead__specs div{ padding:6px 8px; }
  .mhead__specs dd{ font-size:14px; }
  .gcat{ width:70px; }
  .gcat__art{ width:40px; height:40px; }
}
@media (prefers-reduced-motion:reduce){
  .gcat{ transition:none; }
}

/* ==========================================================================
   HOME FEED — group cards and section heads
   ----------------------------------------------------------------------------
   Equal tiles in every row and carousel, the category's own colour as the
   card's accent, and a little more presence for the section heads. Every
   colour is a token or mixed from the category colour with a token, so the
   same rules hold in light and dark.
   ========================================================================== */
.gridcards, .trendrow, .suggestrow{ align-items:stretch; }
.trendrow > .plate, .suggestrow > .plate, .gridcards > .plate{ height:100%; }

.plate{
  border-color:color-mix(in srgb,var(--c) 18%,var(--line));
  background:
    radial-gradient(120% 70% at 100% 0%, color-mix(in srgb,var(--c) 7%,transparent), transparent 60%),
    var(--surface);
}
/* The category's colour along the top edge of the label strip. */
.plate__strip{ box-shadow:inset 0 3px 0 var(--c); padding-top:10px; }
.plate:hover{
  box-shadow:0 14px 30px -14px color-mix(in srgb,var(--c) 55%,transparent), var(--sh-1);
}
.plate:focus-visible{ outline:2px solid var(--c); outline-offset:2px; }
.plate__body{ gap:11px; }
/* The part, on its white, ringed in its category's colour. */
.plate__master .pthumb--plate{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--c) 16%,transparent);
  border-color:color-mix(in srgb,var(--c) 30%,var(--line));
}
/* Two lines at most: a long master name must not make its card taller than
   its neighbours. */
.plate__mname{
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  font-size:18px;
}
@media (min-width:768px){ .plate__mname{ font-size:18.5px; } }
.fitline{
  padding:8px 11px;
  background:color-mix(in srgb,var(--c) 7%,var(--surface-2));
  border-color:color-mix(in srgb,var(--c) 16%,var(--line-soft));
}
.fitline__n{ font-size:24px; }
.plate .mchip--static{
  background:var(--surface-2);
  border-color:color-mix(in srgb,var(--c) 24%,var(--line));
}
.plate .mchip--static.is-master{
  border-color:var(--amber-400);
  background:color-mix(in srgb,var(--amber-400) 10%,var(--surface));
}
.plate__foot{ background:transparent; padding:10px 14px; }
.plate__cta{
  padding:6px 12px; border-radius:var(--r-pill);
  background:color-mix(in srgb,var(--c) 12%,transparent);
  transition:background .16s;
}
.plate:hover .plate__cta{ background:color-mix(in srgb,var(--c) 20%,transparent); }

/* Section heads: a coloured mark and a heavier title, so the feed reads as
   sections rather than one long scroll. */
.feed__head{ margin-bottom:12px; align-items:center; }
.feed__title{ font-size:17px; gap:9px; }
.feed__title svg{
  width:28px; height:28px; padding:6px; border-radius:9px;
  background:color-mix(in srgb,var(--amber-500) 14%,transparent); color:var(--amber-600);
}
@media (max-width:560px){
  .feed__title{ font-size:16px; }
  .feed__note{ flex-basis:100%; margin-left:37px; margin-top:-6px; }
}
@media (prefers-reduced-motion:reduce){
  .plate, .plate__cta{ transition:none; }
}
/* The footer is one line at any card width: the serial gives way with an
   ellipsis, the button never wraps its own label. */
.plate__foot .mono{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.plate__cta{ flex:none; white-space:nowrap; }
@media (max-width:560px){
  .plate__strip{ font-size:10.5px; gap:6px; padding-inline:11px; }
  .plate__cta{ padding:5px 10px; font-size:12.5px; }
}

/* ============================================================
   QUICK BILLING ENTRY — bottom of the desktop category sidebar
   Sticky to the foot of the panel so it is always in reach,
   blue like its tab. The billing screens' own styles are in
   assets/quick-billing.css, loaded only when /billing opens.
   ============================================================ */
.qbentry{
  position:sticky; bottom:0; z-index:3; margin:16px -2px 0; padding:11px 12px;
  display:flex; align-items:center; gap:11px; border-radius:var(--r);
  background:var(--qb-050); color:var(--qb-ink); border:1px solid var(--qb-100);
  box-shadow:0 -10px 18px -8px var(--surface), var(--sh-1);
  transition:background .16s, box-shadow .16s, transform .16s;
}
.qbentry:hover{ background:var(--qb-100); transform:translateY(-1px); }
.qbentry__ico{ width:36px; height:36px; flex:none; border-radius:11px; display:grid; place-items:center; background:var(--qb-grad); color:#fff; box-shadow:var(--qb-glow); }
.qbentry__ico svg{ width:19px; height:19px; }
.qbentry__txt{ display:flex; flex-direction:column; min-width:0; flex:1 1 auto; line-height:1.2; }
.qbentry__txt b{ font-family:var(--f-display); font-size:14.5px; letter-spacing:-.015em; }
.qbentry__txt small{ font-size:11.5px; font-weight:600; color:var(--ink-3); }
.qbentry > svg{ width:16px; height:16px; flex:none; opacity:.7; }
.qbentry.is-on{ background:var(--qb-grad); color:#fff; border-color:transparent; }
.qbentry.is-on .qbentry__txt small{ color:rgba(255,255,255,.8); }
/* the toasts clear the billing cart bar on phones */
body:has(.qb-cartbar) .toasts{ bottom:calc(var(--tabbar-h) + 96px + env(safe-area-inset-bottom)); }
