
/* ============================ TOKENS ============================
   Layer 1 is the only place a colour literal appears in this sheet, and every
   value in it was MEASURED off the logo (site_assets/android-chrome-512x512
   .png, 24,661 coloured pixels) rather than picked: the glass body is hue 180
   at .1-.3 saturation, the checkmark is hue 20-30 at saturation 1.0. So the
   neutral ramp is tinted to 180 instead of grey, and exactly one saturated
   colour exists in the whole system.
   Layer 2 gives those roles, layer 3 assembles the handful of composite
   surfaces. A palette change is an edit to layer 1 and nothing else -- which
   is the point: ~1100 literals used to bypass this block entirely. */
:root{
/* --- layer 1: primitives --- */
--c-teal-900:#12211f;--c-teal-800:#161b1b;--c-teal-700:#1d4340;--c-teal-600:#2b5f5a;
--c-aqua-300:#a9cfc9;--c-aqua-200:#cfe4df;--c-aqua-100:#e7efee;
--c-amber-600:#c25e00;--c-amber-700:#a04d00;
--c-n-800:#2b3a38;--c-n-700:#3d4c4a;--c-n-500:#63726f;--c-n-400:#8a9997;
--c-n-300:#aebcba;--c-n-200:#d2dbda;--c-n-050:#f2f5f5;--c-white:#fff;
/* a third-party payment mark: theirs, not ours to restyle */
--c-bkash:#e2136e;--c-bkash-700:#98094c;
/* channels, so an alpha can be composed without respelling the colour */
--rgb-ink:18,33,31;--rgb-white:255,255,255;--rgb-teal:43,95,90;
--rgb-amber:194,94,0;--rgb-bkash:226,19,110;

/* --- layer 2: roles ---
   AMBER DISCIPLINE. In the logo the amber mark is a small fraction of the
   image and it never touches a dark field. So amber is the accent word, the
   price and the chip hover -- a mark, never a field. Teal carries links,
   labels, buttons and the card accent. On dark surfaces the accent is the
   glass aqua; amber on dark is not a pairing this brand contains. */
--ac:var(--c-amber-600);--acd:var(--c-amber-700);
--amsoft:rgba(var(--rgb-amber),.10);--amline:rgba(var(--rgb-amber),.28);
--teal:var(--c-teal-600);--tealD:var(--c-teal-700);--tealL:var(--c-aqua-100);
--aqua:var(--c-aqua-300);--aquaL:var(--c-aqua-200);
--lnk:var(--c-teal-700);
--tint:rgba(var(--rgb-teal),.08);--tint2:rgba(var(--rgb-teal),.24);
--bg:var(--c-n-050);--card:var(--c-white);--panel:var(--c-teal-800);
--ink:var(--c-teal-900);--ink2:var(--c-n-800);--tx:var(--c-n-700);
--mut:var(--c-n-500);--mut2:var(--c-n-400);--faint:var(--c-n-300);
--rule:var(--c-n-200);--tone:rgba(var(--rgb-ink),.045);
/* on the dark panel */
--onp:var(--c-white);--onp2:rgba(var(--rgb-white),.66);
--onp3:rgba(var(--rgb-white),.30);--onpfill:rgba(var(--rgb-white),.07);
/* ONE accent for the answer deck, so the colour means "this is the pick"
   rather than "this is card three" (owner 2026-08-03) */
--bc:var(--teal);--bs:var(--teal);
/* THREE radii, and the token layer is still the only place one is spelled --
   which was the point of the 2px rule, not the 2px itself. Owner 2026-08-14
   moved the site off square edges; the discipline survives the change. Photos
   and rules stay square via the reset below. A surface that wants a fourth
   value wants one of these three. */
--r:12px;--r-sm:8px;--r-pill:999px;

/* --- layer 3: composite surfaces ---
   Depth is a hairline plus one tight shadow. The 0 14px 34px soft shadow under
   every card was one of the six template tells. */
--sh-hair:inset 0 0 0 1px var(--rule);
--sh-card:inset 0 0 0 1px var(--rule),inset 0 1px 0 rgba(var(--rgb-white),.9),
  0 1px 2px rgba(var(--rgb-ink),.05),0 10px 20px -14px rgba(var(--rgb-ink),.28);
--sh-hover:inset 0 0 0 1px var(--teal),0 2px 4px rgba(var(--rgb-ink),.07),
  0 16px 30px -18px rgba(var(--rgb-ink),.34);
--sh-lift:inset 0 0 0 1px var(--rule),inset 0 1px 0 rgba(var(--rgb-white),.9),
  0 2px 4px rgba(var(--rgb-ink),.06),0 22px 40px -24px rgba(var(--rgb-ink),.40);
/* the header is the ONE glass surface on the site, because it is the only
   place real content scrolls underneath. Over flat paper, glass is just dirty
   translucency -- it was tried on the cards and read as grey smudge. */
--hdr-bg:linear-gradient(180deg,rgba(var(--rgb-white),.72),rgba(246,250,249,.58));
--hdr-sh:inset 0 1px 0 rgba(var(--rgb-white),.95),
  inset 0 -1px 0 rgba(var(--rgb-white),.30),0 1px 12px -4px rgba(var(--rgb-ink),.14);
/* chromatic dispersion: a real edge splits entering light into spectrum.
   Masked to 1px this is the logo's own material, not decoration. */
--prism:linear-gradient(90deg,rgba(143,214,255,.85),rgba(185,242,228,.75) 22%,
  rgba(var(--rgb-white),0) 44%,rgba(255,215,164,.75) 66%,rgba(255,159,201,.7) 84%,
  rgba(143,214,255,.8));
/* a lens hotspot for photo plates, instead of a hard white box */
--plate:radial-gradient(115% 95% at 32% 14%,var(--c-white),
  rgba(240,245,244,.9) 62%,rgba(226,235,233,.75));
/* --- GLASS, sitewide (owner 2026-08-15: "more glassy ios 26 light
   refracting style") ---
   The header has been the one glass surface here since 2026-08-14, for a
   stated reason: glass over flat paper is a grey smudge, because there is
   nothing behind it to refract. So the ground comes FIRST -- a fixed, very low
   contrast bloom in the three prism hues -- and only then do the surfaces turn
   translucent over it. Fills stay above .78 white so 14px body text keeps its
   contrast ratio; that is the line this stops at, not a taste call.
   --glass is for a surface with content under it, --glass-solid for one whose
   own children carry text. */
--glass:linear-gradient(158deg,rgba(var(--rgb-white),.78),
  rgba(var(--rgb-white),.58));
--glass-solid:linear-gradient(158deg,rgba(var(--rgb-white),.88),
  rgba(var(--rgb-white),.72));
--glass-blur:blur(20px) saturate(1.55);
/* the rim is what makes it read as an edge rather than a faded box: a
   specular top line, a teal-tinted hairline, and one soft cast shadow */
--sh-glass:inset 0 1px 0 rgba(var(--rgb-white),.94),
  inset 0 0 0 1px rgba(var(--rgb-teal),.11),
  0 1px 2px rgba(var(--rgb-ink),.04),
  0 20px 38px -26px rgba(var(--rgb-ink),.36);
--sh-glass-hover:inset 0 1px 0 rgba(var(--rgb-white),.96),
  inset 0 0 0 1px rgba(var(--rgb-teal),.30),
  0 2px 4px rgba(var(--rgb-ink),.06),
  0 26px 44px -24px rgba(var(--rgb-ink),.40);
--cta-dark-sh:inset 0 1px 0 rgba(var(--rgb-white),.9),
  0 1px 2px rgba(var(--rgb-ink),.3),0 10px 18px -12px rgba(var(--rgb-ink),.6);
--f-sans:'Libre Franklin','Anek Bangla',system-ui,sans-serif;
--f-display:'Libre Franklin','Anek Bangla',system-ui,sans-serif;
--f-serif:'Libre Franklin','Anek Bangla',system-ui,sans-serif;
/* MONEY IS SET IN ONE FACE. Every other stack puts Libre Franklin first, so a
   taka amount was two typefaces in one word: Franklin has no U+09F3, the sign
   fell through to Anek Bangla, and a big price rendered a Bangla currency mark
   against Latin figures of a different width, weight and axis. Anek Bangla
   draws both, so a price set in it is one design (owner 2026-08-29, "i dont
   like the price font looks"). system-ui still backs it, and there the sign
   falls back as it always did -- but only until the webfont lands. */
--f-price:'Anek Bangla',system-ui,sans-serif}
*{box-sizing:border-box}
/* THE GROUND. Three wide, weak blooms in the prism hues, viewport-anchored so
   they behave like light in the room rather than paint on the page. On the
   background shorthand rather than a ::before, which keeps every stacking
   context on the page exactly as it was. This is what the glass above it is
   refracting; without it the translucency has nothing to show and turns to
   dirt, which is why glass was header-only until now. */
body{margin:0;color:var(--ink);font-family:var(--f-sans);
background:
  radial-gradient(54% 42% at 4% -6%,rgba(169,207,201,.62),transparent 64%),
  radial-gradient(46% 36% at 99% 4%,rgba(255,215,164,.42),transparent 62%),
  radial-gradient(46% 40% at 84% 78%,rgba(169,207,201,.34),transparent 64%),
  radial-gradient(62% 46% at 24% 106%,rgba(143,214,255,.34),transparent 66%),
  var(--bg);
background-attachment:fixed;background-repeat:no-repeat;
font-size:16px;line-height:1.6;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
a{color:var(--lnk);text-decoration:none}a:hover{text-decoration:underline}
::selection{background:var(--tint2)}
/* Photos and rules are square outright; everything else takes --r. The three
   blurred gradient orbs that used to float behind every page are gone -- a
   coloured blob field under a frosted header is a stock template signature,
   not a brand. */
img,figure,picture,svg,hr,table,thead,tbody,tr,td,th{border-radius:0}

/* 980 on desktop: 760 fixed the too-wide cards but made the whole site feel
   cramped on a PC (owner 2026-07-25). The page is wide again; what is capped
   now is the MEASURE of running text (.lead/.vd p/.faq p/.decide p), which is
   what actually reads badly past ~75 characters. Tables and grids use the
   full width. */
.wrap{position:relative;z-index:1;margin:0 auto;padding:8px clamp(16px,4vw,40px) 40px}
/* Owner 2026-08-01, second pass: "nothing on the site should live in a narrow
   card". One shell width for every page, not an opt-in for the hubs -- the
   980px column left half a desktop screen empty and made every card look
   boxed-in. Running text stays readable because the measure caps live on the
   text itself (.lead, .sub, .faq p, .vd p, .decide p), not on the shell. */
.wrap,header.site,.pickcard{max-width:1180px}
/* Real glass reads as glass because of what happens at its EDGE: a specular
   rim where light enters, a dimmer bounce on the far edge, dispersion that
   splits the rim into spectrum, and displacement of what sits behind it. All
   four are here and none of them is a colour blob. */
/* AT REST THE BAR IS NOT GLASS (owner 2026-08-14). Glass is a material you
   see something THROUGH, and at scroll-top there is nothing behind it -- the
   blur was smearing the page background into itself and reading as dirt. So
   the bar is opaque paper until content is actually underneath it, and the
   whole glass stack switches on with html.scr. */
header.site{position:sticky;top:0;z-index:60;display:flex;align-items:center;
justify-content:space-between;gap:10px;margin:14px auto 4px;
padding:11px 14px 11px 12px;border-radius:var(--r);
background:var(--card);
border:0;border-bottom:1px solid var(--rule);
box-shadow:var(--sh-hair);
transition:background .2s linear,box-shadow .2s linear,border-color .2s linear}
html.scr header.site{background:var(--hdr-bg);
backdrop-filter:blur(18px) saturate(1.8);-webkit-backdrop-filter:blur(18px) saturate(1.8);
border-bottom-color:rgba(var(--rgb-teal),.22);
box-shadow:var(--hdr-sh)}
/* dispersion, masked to the bottom edge, where the light actually leaves.
   No light enters an opaque bar, so it fades in with the glass. */
header.site::after{content:"";position:absolute;left:0;right:0;bottom:-1px;
height:1px;pointer-events:none;z-index:3;background:var(--prism);opacity:0;
transition:opacity .2s linear}
html.scr header.site::after{opacity:.55}
/* true refraction where the browser can do it. Behind @supports because a
   browser that ignores url() in backdrop-filter would drop the blur with it.
   The filter itself must render inside <body> -- see _GLASS. */
@supports (backdrop-filter: url(#lensglass)){
html.scr header.site{backdrop-filter:blur(13px) saturate(1.8) url(#lensglass);
-webkit-backdrop-filter:blur(13px) saturate(1.8) url(#lensglass)}}
@media (prefers-reduced-transparency: reduce){
html.scr header.site{backdrop-filter:none;-webkit-backdrop-filter:none;background:var(--card)}
header.site::after{display:none}}
/* no script, no scroll class, no glass -- the bar stays the opaque one, which
   is a complete design rather than a broken one */
@media (prefers-reduced-motion: reduce){
header.site,header.site::after{transition:none}}
/* owner 2026-07-14 round 3: keep brand+nav+cta on ONE row at every width --
   never wrap. The brand shrinks to just the icon on narrow screens (icon+text
   together once there's room) and the nav pills scroll sideways within their
   own lane instead of forcing the header to grow a second row. */
header.site .brand{display:flex;align-items:center;gap:9px;flex:none}
header.site{padding:8px 12px 8px 10px}
header.site .brand img{display:block;width:36px;height:36px}
.btxt{font-size:17.5px}
/* the tagline lives UNDER the wordmark, one string on every page, so the
   header centre is free for real section links (owner 2026-07-25) */
/* ONE line, never wrapped: the wrapped 3-line tagline was what made the bar
   tall (owner 2026-07-25). Below 1080px it drops and the bar is logo-height. */
.btxt em{display:block;font-style:normal;font-family:var(--f-sans);
font-size:10.5px;font-weight:500;letter-spacing:.1px;color:var(--mut);
line-height:1.2;white-space:nowrap;margin-top:1px}
@media(max-width:1080px){.btxt em{display:none}}
.btxt{font-family:var(--f-display);font-weight:700;font-size:20px;
color:var(--ink);letter-spacing:-.4px;white-space:nowrap}
header.site a:hover{text-decoration:none}
/* nav pills centered in the space between brand and CTA, at every width.
   This lane used to be an overflow-x scroller under a fade mask, which only
   ever existed because a fourth pill would not fit a 360px bar. It doesn't
   have a fourth pill; it has three, they fit, and a header that scrolls
   sideways on a phone is a bug rather than a fallback. */
/* Text, not pills (owner 2026-08-14). Three bordered boxes plus a filled
   fourth made the bar read as four competing buttons, and the CTA -- the one
   thing we want pressed -- had to shout over them. Now the links are type and
   the CTA is the only filled object in the header. */
.hnav{display:flex;gap:2px;justify-content:center;min-width:0;flex:1}
.hnav a{position:relative;font-size:14px;font-weight:600;color:var(--mut);
background:none;border:0;padding:8px 12px;white-space:nowrap;flex:none}
.hnav a:hover{color:var(--ink)}
.hnav a.on{color:var(--ink);font-weight:700}
.hnav a.on::after{content:"";position:absolute;left:12px;right:12px;bottom:1px;
height:2px;border-radius:var(--r-pill);background:var(--teal)}
/* the freshness claim, which is the whole product promise, said in the one
   place every page carries. Reassurance ranks below the links and the CTA for
   space, so it is the first thing to go when the bar narrows. */
.hstat{flex:none;display:flex;align-items:center;gap:7px;font-size:12.5px;
font-weight:600;color:var(--mut);white-space:nowrap}
.hstat i{flex:none;width:7px;height:7px;border-radius:50%;background:var(--teal)}
@media(max-width:900px){.hstat{display:none}}
.hcta{flex:none;font-size:13px;font-weight:700;color:var(--onp);
background:var(--teal);border-radius:var(--r-pill);
padding:9px 18px;white-space:nowrap;
box-shadow:inset 0 1px 0 rgba(var(--rgb-white),.18)}
.hcta:hover{background:var(--tealD)}
/* Phones: the bar has to hold brand + 3 links + CTA on a 360px screen, so
   everything shrinks together. Before this the "Compare" pill was sliced in
   half by the CTA (owner, Pixel 5). */
@media(max-width:620px){
/* the wordmark stays on phones (owner 2026-07-25); the room comes from a
   shorter CTA label and tighter pills instead, and the nav lane still scrolls
   under its mask so nothing can ever overlap */
.btxt{font-size:15px}
.btxt em{display:none}
.ctlong{display:none}
header.site{gap:6px;padding:10px 9px;margin:10px auto 4px;border-radius:var(--r)}
header.site .brand img{width:34px;height:34px}
/* stays centred on mobile too: the left-align was only there because a
   centre-justified flex clips both edges when its children overflow, and
   with three pills they no longer do */
.hnav{gap:4px}
.hnav a{font-size:11.5px;padding:6px 10px;font-weight:600}
.hcta{font-size:11.5px;padding:6px 11px}
}
@media(max-width:400px){
.hnav a{font-size:11px;padding:5px 8px}
.hcta{font-size:11px;padding:7px 9px}
.btxt{font-size:14px}
header.site .brand img{width:30px;height:30px}
}

h1{font-family:var(--f-display);font-size:clamp(28px,4.4vw,42px);font-weight:600;
letter-spacing:-1.1px;line-height:1.12;margin:24px 0 8px}
/* The accent word is carried by COLOUR and weight in the one typeface. It was
   an italic serif phrase with a gradient rule under it -- bold sans + italic
   serif phrase is the exact device the other brand uses, and the real overlap
   the cobalt was only drawing attention to. */
h1 em{font-style:normal;font-weight:inherit;color:var(--ac)}
/* one rhythm for every section break on the page: the gaps between "Start
   from a price", "Browse by what matters", "Phone prices" and "Top picks"
   were all different (owner 2026-08-01) */
h2.grp{font-family:var(--f-display);font-size:19px;font-weight:600;
letter-spacing:-.4px;margin:54px 0 2px}
.sub{color:var(--mut);margin:0 0 4px;font-size:14.5px;line-height:1.5;max-width:74ch}

.why{display:flex;gap:11px;padding:15px 17px;border-radius:var(--r);background:var(--tint);margin:22px 0 0}
.why svg{flex:none;margin-top:3px}
.why p{margin:0;font-size:15px;color:var(--ink2);line-height:1.6}
.why b{color:var(--ac)}

.hero{position:relative;background:var(--card);
border-radius:var(--r);padding:clamp(20px,3vw,30px);overflow:hidden;margin:18px 0 14px;
box-shadow:var(--sh-lift)}
.pick{display:flex;gap:16px;position:relative}
.pick>img{width:clamp(88px,11vw,104px);height:clamp(116px,14vw,136px);object-fit:contain;
flex:none;border-radius:var(--r);background:var(--bg)}
.pick .top{flex:1;min-width:0}
.brand{font-size:13px;color:var(--mut2);font-weight:500}
.brand img{height:18px;width:auto;max-width:132px;object-fit:contain;
  object-position:left;display:block}
.hero h2{font-family:var(--f-display);margin:1px 0 0;font-size:clamp(21px,2.4vw,26px);
font-weight:700;letter-spacing:-.4px;line-height:1.12}
.badge{float:right;font-size:11.5px;font-weight:700;padding:5px 11px;border-radius:var(--r);
white-space:nowrap;color:var(--tealD);background:var(--tealL);margin-left:8px}

.price{display:flex;align-items:flex-end;gap:11px;margin-top:20px;flex-wrap:wrap}
.price .amt{font-size:clamp(30px,3.6vw,40px);font-weight:300;letter-spacing:-1.6px;
color:var(--ink);line-height:1}
.price .at{font-size:13.5px;color:var(--mut);margin-bottom:5px}
.unconf{color:var(--lnk);font-size:12.5px;font-weight:600}

.chips{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0 0}
.chip{font-size:12.5px;font-weight:600;padding:5px 12px;border-radius:var(--r);
background:rgba(var(--rgb-ink),.055);color:var(--tx)}
.chip.ok{background:var(--tealL);color:var(--tealD)}
.chip.warn{background:var(--amsoft);color:var(--lnk)}
/* import market. Shared with device pages (they load this sheet first) so a
   "China from X" chip looks the same on a guide, a /vs page and /phone/. */
.chip.reg{background:var(--tealL);color:var(--tealD)}

/* tables scroll on a phone; say so instead of letting the columns look cut.
   Shared: the guide comparison table is the full spec strings now, so it needs
   the hint the device page's shop table already had. */
.swipe{display:none;margin:10px 0 -6px;font-size:12.5px;color:var(--mut)}
@media(max-width:720px){.swipe{display:block}}

/* the full GSMArena sheet: device pages AND /vs pages fold it away here */
/* the whole sheet is reference material, not reading material: folded away */
.specdump{margin:16px 0 0}
.specdump>summary{cursor:pointer;list-style:none;display:flex;align-items:center;
  justify-content:space-between;gap:10px;padding:13px 17px;border-radius:var(--r);
  background:var(--card);font-size:14px;font-weight:700;
  color:var(--lnk);box-shadow:var(--sh-hair)}
.specdump>summary::-webkit-details-marker{display:none}
.specdump>summary:hover{background:var(--tealL)}
.specdump .chev{font-size:20px;line-height:1;transition:transform .18s}
.specdump[open] .chev{transform:rotate(90deg)}
.srccredit{font-size:12px;color:var(--mut);margin:14px 2px 4px;line-height:1.6}
.srccredit a{color:var(--lnk);font-weight:600}
.specgrp{margin:18px 0 0;padding:14px 16px 4px;border-radius:var(--r);
  background:var(--card);
  box-shadow:var(--sh-card)}
.specgrp h3{font-size:11px;text-transform:uppercase;letter-spacing:.8px;
  color:var(--mut);font-weight:700;margin:0 0 4px}
table.rawspec{width:100%;border-collapse:collapse;font-size:13.5px;
  table-layout:fixed}
table.rawspec th{width:132px;text-align:left;vertical-align:top;font-weight:600;
  color:var(--ink);padding:9px 12px 9px 0;
  border-bottom:1px solid var(--rule)}
table.rawspec td{vertical-align:top;color:var(--tx);padding:9px 0;
  border-bottom:1px solid var(--rule);
  overflow-wrap:anywhere;word-break:break-word;line-height:1.55}
table.rawspec tr:last-child th,table.rawspec tr:last-child td{border-bottom:none}
@media(max-width:640px){
.specgrp{padding:13px 14px 4px}
table.rawspec,table.rawspec tbody,table.rawspec tr,
table.rawspec th,table.rawspec td{display:block;width:auto}
table.rawspec th{padding:10px 0 0;border-bottom:none;font-size:11px;
  text-transform:uppercase;letter-spacing:.6px;color:var(--mut)}
table.rawspec td{padding:2px 0 10px}
}

.fit{padding:15px 16px;border-radius:var(--r);background:rgba(var(--rgb-ink),.035);margin-top:20px}
.fit .r{display:flex;justify-content:space-between;font-size:13.5px;margin-bottom:11px}
.fit .r .lab{color:var(--mut)}
.bar{position:relative;height:8px}
.bar .tk{position:absolute;inset:0;border-radius:var(--r);background:rgba(var(--rgb-ink),.08)}
.bar .ceil{position:absolute;top:-3px;height:14px;width:2px;border-radius:var(--r);transform:translateX(-50%);background:var(--faint)}
.bar .dot{position:absolute;top:-3px;width:14px;height:14px;border-radius:var(--r);transform:translateX(-50%);
background:var(--ac);box-shadow:0 0 0 2px var(--card)}

/* the shell is 1180 now, so running text has to carry its own measure or the
   guide verdict runs the full width of a desktop */
.verdict{margin:18px 0 0;font-size:15px;color:var(--ink2);line-height:1.6;
max-width:76ch}

/* The runner-up card is the hero at card size: photo | identity and reasoning
   | money and the button. Same three-part reading order, so the list does not
   change shape halfway down (owner 2026-08-15). */
.cards{display:grid;grid-template-columns:1fr;gap:11px;margin-top:14px}
.card{display:grid;grid-template-columns:auto clamp(84px,9vw,120px) minmax(0,1fr)
  clamp(196px,20vw,244px);align-items:start;gap:clamp(12px,1.8vw,20px);
padding:18px 20px;border-radius:var(--r);
background:var(--card);box-shadow:var(--sh-card)}
.card .num{width:27px;height:27px;border-radius:var(--r);background:rgba(var(--rgb-ink),.055);color:var(--mut);
font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex:none;margin-top:2px}
.cmedia{aspect-ratio:3/4}
.cmedia img{width:100%;height:100%;object-fit:contain;display:block}
.cbody{min-width:0}
.ctop{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.cid{min-width:0}
.card .nm{font-family:var(--f-display);font-size:17px;font-weight:700;
letter-spacing:-.3px;color:var(--ink);line-height:1.2}
.card .hp{font-size:13px;color:var(--mut);margin-top:3px}
.card .chips{margin-top:10px}
.card .chip{font-size:11.5px;padding:4px 10px}
.card .cvd{margin:11px 0 0;font-size:14px;color:var(--ink2);line-height:1.55}
.card .kf{margin-top:9px}
/* the money column: the price, what it does to the budget, and the button --
   the three things a reader compares between cards, in one place per card */
.cbuy{display:flex;flex-direction:column;align-items:flex-start;gap:3px;
padding:14px 15px 15px;border-radius:var(--r);background:var(--bg);
box-shadow:var(--sh-hair)}
.camt{font-family:var(--f-price);font-variant-numeric:tabular-nums;font-size:clamp(19px,1.9vw,23px);font-weight:700;
letter-spacing:-.9px;line-height:1.15;color:var(--ac)}
.cbuy .at{font-size:12px;color:var(--mut)}
.cfit{font-size:12.5px;font-weight:700;line-height:1.35;margin-top:3px}
.cbuy .dbtn.wide{margin-top:12px;padding:12px 14px;font-size:13.5px}
@media(max-width:860px){
.card{grid-template-columns:auto clamp(72px,13vw,104px) minmax(0,1fr)}
.cbuy{grid-column:2/-1;flex-direction:row;align-items:center;flex-wrap:wrap;
gap:4px 14px}
.cbuy .dbtn.wide{margin-top:0;margin-left:auto;width:auto}}

.cta{display:inline-block;background:var(--teal);color:var(--onp);
font-weight:600;font-size:15.5px;padding:15px 26px;border-radius:var(--r);margin:20px 0 4px;
border:1.5px solid var(--teal);box-shadow:inset 0 1px 0 rgba(var(--rgb-white),.18)}
.cta:hover{text-decoration:none;background:var(--tealD);border-color:var(--tealD)}

.stretch{display:flex;align-items:center;gap:15px;padding:18px 20px;margin-top:14px;border-radius:var(--r);
background:var(--tint);box-shadow:var(--sh-hair)}
.stretch>img{width:52px;height:68px;object-fit:contain;border-radius:var(--r);background:var(--bg);flex:none}
.stretch .lab{font-size:11px;font-weight:700;color:var(--lnk);letter-spacing:1.2px;text-transform:uppercase}
.stretch .nm{font-size:17px;font-weight:700;color:var(--ink);margin-top:3px}
.stretch .rs{font-size:14px;color:var(--mut);margin-top:3px;line-height:1.5}


nav.linkrow{display:flex;flex-wrap:wrap;gap:8px;margin:20px 0}
nav.linkrow a{font-size:13px;font-weight:600;color:var(--mut);background:var(--card);
border:1px solid var(--rule);border-radius:var(--r);padding:6px 13px}
nav.linkrow a:hover{text-decoration:none;color:var(--lnk)}

footer.site{margin-top:28px;padding:18px 0;color:var(--mut);font-size:12.5px;line-height:1.6;
border-top:1px solid var(--rule)}
footer.site a{color:var(--mut);text-decoration:underline}

/* rich dark footer (owner 2026-07-19); picker card inline in body (owner 2026-07-20) */
.footwrap{position:relative;z-index:1;margin-top:20px}
/* This was a royal-blue radial field with white bold sans over it: the single
   closest thing on the site to the cover we were accused of copying. Flat
   near-neutral ink now -- no glow, no colour ramp, one hairline of dispersion
   along the top edge and nothing else. */
.pickcard{margin:36px auto;border-radius:var(--r);position:relative;
overflow:hidden;padding:clamp(30px,5vw,46px);color:var(--onp);
background:var(--panel);
box-shadow:none}
.pickcard::after{content:"";position:absolute;left:0;right:0;top:0;height:1px;
pointer-events:none;background:var(--prism);opacity:.5}
.pickcard{display:flex;align-items:center;justify-content:space-between;gap:26px}
.pcopy{min-width:0}
.pickcard h2{font-family:var(--f-display);font-size:clamp(25px,4.4vw,40px);font-weight:700;
letter-spacing:-1.4px;line-height:1.06;margin:0;max-width:14ch}
/* ON DARK the accent is the glass aqua: in the logo the amber only ever sits
   on pale glass, and amber over ink read muddy when it was tried. */
.pickcard h2 em,.mfh h3 em{font-style:normal;font-weight:inherit;color:var(--aqua)}
.pickcard p{color:var(--onp2);font-size:15px;line-height:1.55;margin:14px 0 24px;max-width:44ch}
/* The one CTA on a dark field. An aqua plate alone read flat, so it gets the
   header's edge physics -- specular rim, hairline, one tight shadow -- and
   reads as material rather than a painted rectangle (owner 2026-08-03). */
.pickcard .cta,.mfeat .cta{background:var(--aqua);color:var(--panel);
border-color:var(--aqua);box-shadow:var(--cta-dark-sh);margin:0}
.pickcard .cta:hover,.mfeat .cta:hover{background:var(--c-white);border-color:var(--c-white)}
/* brand block filling the right of the CTA (owner 2026-07-25) */
.pbrand{flex:none;display:flex;flex-direction:column;align-items:center;gap:9px;
text-align:center;padding-left:26px;border-left:1px solid rgba(var(--rgb-white),.14)}
/* The mark is already transparent (corner alpha 0). The white plate was
   solving a contrast problem the old navy field created; the panel is
   near-neutral now, so the mark sits on it directly. */
.pbrand img{background:none;padding:0;box-shadow:none}
.pbtxt{display:flex;flex-direction:column;gap:3px;align-items:center}
.pbn{font-family:var(--f-display);font-size:22px;font-weight:700;letter-spacing:-.5px;color:var(--onp)}
.pbt{font-size:12px;line-height:1.4;color:var(--onp3);max-width:19ch}
@media(max-width:720px){.pickcard{flex-direction:column;align-items:flex-start;gap:24px}
.pbrand{flex-direction:row;align-items:center;text-align:left;padding:18px 0 0;
padding-left:0;border-left:0;border-top:1px solid rgba(var(--rgb-white),.14);width:100%;gap:14px}
.pbrand img{width:52px;height:52px;border-radius:var(--r)}
.pbtxt{align-items:flex-start}
.pbn{font-size:18px}.pbt{font-size:11.5px;max-width:30ch}}
.bigfoot{background:var(--panel);color:var(--onp2);border-radius:var(--r) var(--r) 0 0;
padding:52px clamp(20px,5vw,56px) 0}
.bigfoot .inner{max-width:940px;margin:0 auto;display:grid;
grid-template-columns:1.7fr 1fr 1fr 1fr;gap:34px;padding-bottom:38px}
.bigfoot .brand{display:flex;align-items:center;gap:11px;color:var(--onp);
font-family:var(--f-display);font-size:22px;font-weight:700}
.bigfoot .brand img{width:46px;height:46px;border-radius:var(--r)}
.bigfoot .desc{margin:15px 0 0;font-size:13px;line-height:1.65;max-width:34ch}
.bigfoot .ct{margin-top:14px;font-size:13px}.bigfoot .ct .lb{color:var(--onp3);font-size:11px}
.bigfoot .fh{color:rgba(var(--rgb-white),.86);font-size:12px;font-weight:600;letter-spacing:.2px;margin:0 0 15px}
.bigfoot ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}
.bigfoot ul a{color:var(--onp2);font-size:13.5px;text-decoration:none}
.bigfoot ul a:hover{color:var(--onp);text-decoration:none}
.bigfoot .copy{border-top:1px solid rgba(var(--rgb-white),.08);max-width:940px;margin:0 auto;
padding:20px 0;text-align:center;font-size:12.5px;color:var(--onp3)}
/* the trademark notice: quieter than the copyright it sits under, and on its
   own line so it reads as a disclaimer rather than part of the byline */
.bigfoot .tm{display:block;max-width:660px;margin:7px auto 0;font-size:11.5px;
line-height:1.55;text-wrap:pretty}
@media(max-width:640px){.bigfoot .inner{grid-template-columns:1fr 1fr;gap:26px}
.pickcard{margin:28px auto}.bigfoot{padding-top:52px}}

@media(max-width:560px){
/* owner 2026-07-14 round 2: column-stacking the hero orphaned the image in a
   corner of dead space. Keep image BESIDE the title on mobile, just smaller,
   and let the badge sit inline above the brand instead of floating right.
   owner 2026-07-17: images were proportionally TOO small on mobile, leaving
   dead space next to the text column -- sized up hero + card images. */
.pick{gap:13px}
.pick>img{width:98px;height:130px}
.badge{float:none;display:inline-block;margin:0 0 7px}
.hero h2{font-size:20px}
.price{margin-top:14px}
.price .amt{font-size:29px;letter-spacing:-1.2px}
/* the card is a grid, so it reflows by narrowing its columns -- the old rules
   here (flex-wrap, .card>img, .fitlab) addressed the flex row it stopped being
   and did nothing at all (owner 2026-08-15: "fit the whole thing in mobile") */
.card{grid-template-columns:auto 72px minmax(0,1fr);gap:11px;padding:15px}
.card .num{width:24px;height:24px;font-size:12px;margin-top:0}
.card .nm{font-size:15.5px}
.ctop{gap:9px}
.card .cvd{font-size:13.5px}
.cbuy{padding:11px 12px}
.decide{padding:15px 15px 9px}
.tblwrap{margin-top:18px}
}
@media(prefers-reduced-motion:no-preference){
.hero,.card,.stretch{animation:krise .5s cubic-bezier(.2,.7,.2,1) both}
@keyframes krise{from{opacity:0;transform:translateY(13px)}to{opacity:1;transform:none}}}

/* breadcrumbs, owner reference 2026-07-25: house icon for home, chevron
   separators, muted ancestors, the current page in solid bold ink */
nav.crumbs{display:flex;align-items:center;flex-wrap:wrap;gap:7px;
font-size:13.5px;font-weight:500;color:var(--mut);margin:20px 0 -10px}
nav.crumbs a{display:inline-flex;align-items:center;gap:6px;color:var(--mut);
border-radius:var(--r);padding:2px 4px;transition:color .15s,background .15s}
nav.crumbs a:hover{color:var(--ink);background:rgba(var(--rgb-ink),.05);text-decoration:none}
nav.crumbs a.home{padding:3px 5px}
nav.crumbs svg{display:block;flex:none}
nav.crumbs .sep{color:var(--faint);display:flex;align-items:center}
nav.crumbs .here{color:var(--ink);font-weight:700;padding:2px 4px}
.lead{max-width:74ch;font-size:16.5px;color:var(--ink2);line-height:1.6;margin:10px 0 2px}
.lead b{color:var(--ac)}
.kf{font-size:12.5px;color:var(--mut);margin-top:8px;line-height:1.55}
/* RAM/storage configs and what each one costs: one price + one RAM figure
   described two different products before this (owner 2026-07-26). */
.vlist{margin-top:5px}
.vlab{display:inline-block;font-weight:700;color:var(--ink2);margin-right:7px}
.tblwrap{overflow-x:auto;margin:18px 0 0;border-radius:var(--r);background:var(--card);
box-shadow:var(--sh-card)}
/* No edge fade: CSS cannot tell whether the table actually overflows, so on a
   desktop where it fits the fade just washed out the last column for nothing
   (owner 2026-07-25). The "Swipe the table sideways" line carries the message
   on phones, where it is true. */
.tblwrap{-webkit-overflow-scrolling:touch}
/* ===================== GO LINKS =====================
   Every "take me there" on the site is one shape (owner 2026-08-15: links
   "should be much better visible and clickable ... sitewide"). A quiet 12.5px
   text link with an arrow after it was the same weight as the body copy it sat
   under, so the thing the page is FOR read as a footnote. These are pills with
   a real hit area, a tinted field that fills on hover, and an arrow that steps
   forward -- one rule, so a guide, a device page and a hub cannot disagree
   about what a link that goes somewhere looks like. */
.dgo,.rgo,.ksmore{display:inline-flex;align-items:center;gap:7px;
font-size:13px;font-weight:700;line-height:1;color:var(--lnk);
background:var(--tealL);border-radius:var(--r-pill);padding:10px 16px;
transition:background .16s,color .16s,box-shadow .16s}
.ksmore{margin-top:14px}
.rgo:hover,.ksmore:hover,a.dcell:hover .dgo{
text-decoration:none;background:var(--teal);color:var(--onp);
box-shadow:inset 0 1px 0 rgba(var(--rgb-white),.18)}
.dgo i,.rgo i,.ksmore i{font-style:normal;display:inline-block;
transition:transform .16s}
.rgo:hover i,.ksmore:hover i,a.dcell:hover .dgo i{transform:translateX(3px)}
/* the budget rail: the same pill, sized for a grid of sixteen of them */
nav.linkrow a{display:flex;align-items:center;justify-content:center;
font-size:13px;font-weight:700;color:var(--lnk);background:var(--tealL);
padding:11px 10px;transition:background .16s,color .16s}
nav.linkrow a:hover{text-decoration:none;background:var(--teal);color:var(--onp)}
/* the shop table is a data surface people actually read down, so it gets a
   real header band, row hover and tabular figures rather than plain rules
   (owner 2026-08-01) */
table.cmp{border-collapse:collapse;width:100%;min-width:600px;font-size:13.5px}
table.cmp thead th{position:sticky;top:0;z-index:1;font-size:10.5px;
text-transform:uppercase;letter-spacing:.8px;color:var(--mut);font-weight:700;
text-align:left;padding:13px 16px;background:var(--bg);
border-bottom:1px solid var(--rule)}
table.cmp td{padding:12px 16px;border-bottom:1px solid var(--rule);
white-space:nowrap;color:var(--tx);font-variant-numeric:tabular-nums}
table.cmp tbody tr{transition:background .12s}
table.cmp tbody tr:hover{background:var(--tint)}
table.cmp tr:last-child td{border-bottom:none}
table.cmp td.ph{font-weight:600;color:var(--ink);font-variant-numeric:normal}
.decide{margin:22px 0 0;padding:18px 20px 12px;border-radius:var(--r);background:var(--card);
box-shadow:var(--sh-card)}
.decide h2{font-family:var(--f-display);font-size:17px;font-weight:600;letter-spacing:-.3px;margin:0}
.decide ul{margin:8px 0 0;padding:0;list-style:none}
.decide li{padding:8px 0;font-size:14.5px;border-bottom:1px solid var(--rule);line-height:1.5}
.decide li:last-child{border-bottom:none}
.decide b{color:var(--ink)}
/* FAQ, owner round 2 (2026-07-25): no tinted field behind the section, only
   the OPEN card is highlighted and the closed ones sit back, and one quiet
   text link instead of a pair of buttons. */
.faq{margin:34px 0 0}
.fhead{text-align:center;margin-bottom:20px}
.faq .eyebrow{display:block;font-size:12.5px;font-weight:600;color:var(--mut);
letter-spacing:.2px;margin-bottom:10px}
/* text-wrap:balance keeps a lone orphan word off the last line ("... ask
   / us"), and the wider measure lets most headings sit on two full lines */
.faq h2{font-family:var(--f-display);font-size:clamp(24px,3.2vw,33px);font-weight:700;
letter-spacing:-1.1px;line-height:1.16;margin:0 auto;max-width:26ch;color:var(--ink);
text-wrap:balance}
/* a real button: the plain blue link was easy to miss (owner 2026-07-25) */
.fmore{display:inline-flex;align-items:center;gap:8px;margin-top:18px;
font-size:14.5px;font-weight:700;color:var(--ink);background:var(--card);
border-radius:var(--r);padding:11px 22px;
box-shadow:var(--sh-card)}
.fmore:hover{text-decoration:none;color:var(--ac);
box-shadow:inset 0 0 0 1px var(--amline)}
.fmore span{transition:transform .18s}
.fmore:hover span{transform:translateX(3px)}
/* closed = muted and flat; open = white, lifted, full-ink */
.faq details{background:none;border-radius:var(--r);margin-top:10px;
padding:0 20px;box-shadow:var(--sh-hair);transition:background .16s}
.faq details:hover{background:var(--tone)}
.faq details[open]{background:var(--card);box-shadow:var(--sh-lift)}
.faq summary{display:flex;align-items:center;justify-content:space-between;gap:14px;
cursor:pointer;font-weight:600;font-size:16px;color:var(--mut);padding:17px 0;
position:relative;list-style:none}
.faq details[open] summary{color:var(--ink)}
.faq .chev{color:var(--faint);display:flex;transition:transform .2s}
.faq details[open] .chev{color:var(--lnk);transform:rotate(180deg)}
.faq summary::-webkit-details-marker{display:none}
/* the answer needs room under it -- the card used to end on the last line */
.faq p{margin:-4px 0 0;padding:0 30px 22px 0;font-size:15px;color:var(--mut);
line-height:1.65;max-width:74ch}

/* The four facts under the hero were flat grey pills of the same weight as
   every other chip on the site, so the one number that matters (how many
   guides are live) read as decoration (owner 2026-08-01: "place and design
   these chips much better"). They are cards now: a tinted icon, the figure in
   full ink, the label under it. */
.statrow{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
gap:11px;margin:40px 0 0}
@media(max-width:840px){.statrow{grid-template-columns:1fr 1fr}}
.st{display:flex;align-items:center;gap:12px;padding:13px 16px;
border-radius:var(--r);background:var(--card);
box-shadow:var(--sh-card)}
.st .si{display:flex;align-items:center;justify-content:center;flex:none;
width:34px;height:34px;border-radius:var(--r);background:var(--tealL);
color:var(--teal)}
/* the one warm mark in the row, and the only amber on this page: same share
   of the surface the checkmark has in the logo */
.st.ok .si{background:var(--amsoft);color:var(--lnk)}
.st b{display:block;font-family:var(--f-display);font-size:16.5px;
font-weight:700;letter-spacing:-.4px;color:var(--ink);line-height:1.15}
.st span{display:block;font-size:12px;color:var(--mut);margin-top:2px}
@media(max-width:560px){
.statrow{grid-template-columns:1fr 1fr;gap:8px}
.st{padding:11px 12px;gap:9px}
.st .si{width:28px;height:28px;border-radius:var(--r)}
.st b{font-size:14.5px}.st span{font-size:11px}}
.matrix{margin:16px 0 0}
/* owner 2026-08-01: every category was an identical lane of grey chips, so
   the one most people want was no easier to find than the rest. "Best
   overall" is now a featured card and the others are a grid, each identified
   by its OWN #1 phone rather than by a 15px icon alone. */
.mfeat{display:grid;grid-template-columns:minmax(0,1fr) 230px;gap:0;
border-radius:var(--r);overflow:hidden;position:relative;
background:var(--panel)}
.mfeat::after{content:"";position:absolute;left:0;right:0;top:0;height:1px;
pointer-events:none;z-index:2;background:var(--prism);opacity:.5}
.mfc{padding:clamp(20px,3vw,30px)}
.mfh{display:flex;align-items:center;gap:11px}
.mfh h3{margin:0;font-family:var(--f-display);font-size:clamp(21px,2.6vw,26px);
font-weight:700;letter-spacing:-.6px;color:var(--onp)}
/* The tile carried width/height/background but no display, so it stayed an
   inline box: the sizes did nothing and the 15px glyph sat on the text
   baseline in the corner instead of the middle (owner 2026-08-01). */
.mi{display:inline-flex;align-items:center;justify-content:center;flex:none;
width:30px;height:30px;border-radius:var(--r);background:var(--tealL);
color:var(--teal)}
.mi svg{display:block}
.mfh .mi{background:rgba(var(--rgb-white),.14);color:var(--onp);width:34px;height:34px;
border-radius:var(--r)}
.mfs{margin:7px 0 0;font-size:13.5px;color:var(--onp2)}
.mfchips{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
/* chips ON the dark panel are outlined, not filled: a white glass fill over
   ink is the grey smudge that killed this block in the first draft */
.mfchips a{display:inline-flex;align-items:baseline;gap:5px;font-size:13.5px;
font-weight:700;letter-spacing:-.2px;color:var(--onp);border-radius:var(--r);
padding:8px 15px;background:var(--onpfill);
box-shadow:inset 0 0 0 1px var(--onp3);transition:background .16s}
.mfchips a b{font-size:9.5px;font-weight:700;letter-spacing:.6px;
text-transform:uppercase;color:var(--onp2)}
.mfchips a:hover{text-decoration:none;background:rgba(var(--rgb-white),.14);
box-shadow:inset 0 0 0 1px var(--c-white)}
.mfchips a.all{background:var(--aqua);color:var(--panel);
box-shadow:inset 0 0 0 1px var(--aqua)}
.mfchips a.all:hover{background:var(--c-white);
box-shadow:inset 0 0 0 1px var(--c-white);transform:none}
/* multiply cannot help on the dark card -- it would drag the phone itself
   down into the ink. The photo keeps a light plate to sit on, which reads as
   a deliberate frame rather than a stray white rectangle; the plate is the
   lens hotspot now, not a slab with a 40px shadow under it. */
.mfimg{display:flex;align-items:center;justify-content:center;padding:20px}
.mfimg img{max-height:200px;max-width:100%;width:auto;object-fit:contain;
border-radius:var(--r);background:var(--plate);padding:8px;
box-shadow:inset 0 1px 0 rgba(var(--rgb-white),.95)}

.mgrid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
.mcard{display:flex;gap:14px;padding:15px 16px;border-radius:var(--r);
background:var(--card);
box-shadow:var(--sh-card);
transition:box-shadow .18s,transform .18s cubic-bezier(.4,0,.2,1)}
.mcard:hover{box-shadow:var(--sh-hover)}
/* Owner 2026-08-01: "the images have a lil bit white border, breaking the
   immersion". The pipeline normalises every photo onto a white 3:4 canvas, so
   on a tinted tile that canvas drew a bright rectangle around the phone.
   multiply makes white transparent against any lighter backdrop -- no
   re-processing of 500-odd images, and it fixes every surface at once. */
.mcimg{display:flex;align-items:center;justify-content:center;flex:none;
width:78px;border-radius:var(--r);background:var(--card)}
.mcimg img{max-height:96px;width:auto;object-fit:contain;
mix-blend-mode:multiply}
.mcb{min-width:0;flex:1 1 auto}
.mch{display:flex;align-items:center;gap:9px}
.mch h3{margin:0;font-family:var(--f-display);font-size:16.5px;font-weight:600;
letter-spacing:-.3px;color:var(--ink)}
.mcchips{display:flex;flex-wrap:wrap;gap:7px;margin-top:11px}
.mcall{display:inline-block;margin-top:11px;font-size:12.5px;font-weight:700;
color:var(--lnk)}
/* owner 2026-08-01: the chips were "good enough but could be better". They
   read as grey pills of running text; what a buyer scans for is the NUMBER.
   So "Under" drops to a small caps label and the figure carries the weight,
   and the pill lifts on hover instead of only inverting. */
.mcchips a{flex:none;display:inline-flex;align-items:baseline;gap:5px;
font-size:14px;font-weight:700;letter-spacing:-.2px;color:var(--ink2);
background:var(--card);border:1px solid var(--rule);
border-radius:var(--r);padding:7px 13px;
transition:transform .16s cubic-bezier(.4,0,.2,1),box-shadow .16s,background .16s}
.mcchips a b{font-size:10px;font-weight:700;letter-spacing:.7px;
text-transform:uppercase;color:var(--mut);transition:color .16s}
/* a filled chip is a field, not a mark, so the fill is teal -- amber stays
   for the small marks (the figure, the accent word, the price) */
.mcchips a:hover{text-decoration:none;color:var(--onp);
background:var(--teal);border-color:var(--teal);
box-shadow:none}
.mcchips a:hover b{color:rgba(var(--rgb-white),.78)}

/* ---- /best directory: EVERY guide, not a five-chip sample. The hub used to
   render the homepage matrix, so its own "View all" pointed back at itself and
   the other 60-odd guides had no link anywhere on the site (owner 2026-08-01:
   "view all leads to nothing"). ---- */
.dirnav{display:flex;flex-wrap:wrap;gap:8px;margin:18px 0 0}
.dirnav a{display:inline-flex;align-items:center;gap:8px;font-size:13.5px;
font-weight:700;color:var(--tx);background:var(--card);
border:1px solid var(--rule);border-radius:var(--r);padding:8px 15px;
transition:transform .16s cubic-bezier(.4,0,.2,1),box-shadow .16s,color .16s}
.dirnav a:hover{text-decoration:none;color:var(--lnk);transform:translateY(-2px);
box-shadow:0 6px 16px rgba(var(--rgb-ink),.09)}
.dirnav a i{font-style:normal;font-size:11.5px;font-weight:700;color:var(--mut)}
.dirsec{margin:34px 0 0;scroll-margin-top:96px}
.dirsec .dh{display:flex;align-items:center;gap:11px}
.dirsec .dh h2{margin:0;font-family:var(--f-display);font-size:20px;
font-weight:700;letter-spacing:-.5px;color:var(--ink)}
.dirsec .dh .mi{width:32px;height:32px}
.dirsub{margin:6px 0 0;font-size:13.5px;color:var(--mut)}
.dirgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(228px,1fr));
gap:11px;margin-top:14px}
.dircard{display:flex;align-items:center;gap:13px;padding:12px 14px;
border-radius:var(--r);background:var(--card);
box-shadow:var(--sh-card);
transition:transform .16s cubic-bezier(.4,0,.2,1),box-shadow .16s}
.dircard:hover{text-decoration:none;transform:translateY(-3px);
box-shadow:0 10px 24px rgba(var(--rgb-ink),.11),inset 0 0 0 1px var(--tint2)}
.dircard img{width:42px;height:56px;object-fit:contain;flex:none;
mix-blend-mode:multiply}
.dircard .db{min-width:0;flex:1}
.dircard .dl{display:block;font-size:9.5px;font-weight:700;letter-spacing:1px;
text-transform:uppercase;color:var(--mut)}
.dircard .dv{display:block;font-size:17px;font-weight:700;letter-spacing:-.6px;
color:var(--ink);line-height:1.2}
.dircard .dn{display:block;font-size:12.5px;font-weight:600;color:var(--mut);
margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media(max-width:560px){
.dirgrid{grid-template-columns:1fr 1fr;gap:8px}
.dircard{gap:9px;padding:10px 11px}
.dircard img{width:34px;height:46px}
.dircard .dv{font-size:15px}
.dircard .dn{display:none}
}

/* ---- home hero: the promise on the left, the picker working on the right.
   SCOPED to .home, and it must stay that way. `.hero` is the shared card that
   the guides, the /vs duels and every /phone device page also draw, so an
   unscoped grid here laid ALL of them out in two columns with the right half
   empty -- a guide's verdict ended up beside its own pick card and the device
   hero was squeezed into 155px (owner 2026-08-01).
   No card, either: the homepage is one continuous body, not a white box
   floating on a background (owner 2026-08-01). ---- */
.hero.home{display:grid;grid-template-columns:minmax(0,.95fr) minmax(0,1fr);
gap:clamp(24px,4.5vw,56px);align-items:center;margin:10px 0 0;
background:none;box-shadow:none;border-radius:0;padding:0;overflow:visible}
.hero.home h1{font-size:clamp(34px,5.1vw,58px);letter-spacing:-2.2px;
line-height:1.02;margin:0 0 16px;text-wrap:balance}
/* No rule under the accent word. A gradient underline beneath an italic serif
   phrase is the signature this reskin exists to remove. */
.hero.home .lead{font-size:clamp(15.5px,1.4vw,17.5px);max-width:44ch;
color:var(--tx);line-height:1.55}
.heroc{min-width:0}
.herocta{display:flex;flex-wrap:wrap;gap:11px;margin:26px 0 0}
.hbtn{display:inline-flex;align-items:center;gap:9px;font-size:15.5px;
font-weight:700;letter-spacing:-.2px;padding:15px 27px;border-radius:var(--r);
color:var(--onp);background:var(--teal);border:1.5px solid var(--teal);
box-shadow:inset 0 1px 0 rgba(var(--rgb-white),.18);transition:background .16s,box-shadow .16s}
.hbtn i{font-style:normal;transition:transform .18s}
.hbtn:hover i{transform:translateX(4px)}
.hbtn:hover{text-decoration:none;background:var(--tealD);border-color:var(--tealD)}
.hbtn.ghost{color:var(--ink);background:var(--card);border:1.5px solid var(--teal);
box-shadow:none}
.hbtn.ghost:hover{background:var(--teal);color:var(--onp)}

/* Every budget card stays on screen for the whole loop and lights up on its
   own turn. No element ever moves as part of a group: the old version
   cross-faded one big .hscene per budget, which read as the entire block
   swiping up rather than as cards that are alive (owner 2026-08-01, and the
   brand-leak check in test_device_pages reads this sheet -- keep phone brand
   names out of these comments). What animates now is a white
   sheet fading in behind ONE card, its own label swapping, its own bar
   filling -- and, below, the answer cards cross-fading one into the next. */
.herodemo{position:relative;min-width:0}
/* Side by side, not stacked: budgets on the left, the answer they produce
   right beside them (owner 2026-08-01). Vertically it was twice as tall as
   the copy it sat next to. */
.hstage{display:grid;grid-template-columns:minmax(0,.74fr) minmax(0,1fr);
gap:clamp(12px,1.6vw,20px);align-items:center}
.hbudcol,.hanscol{min-width:0}
.hlab{display:block;font-size:10px;font-weight:700;letter-spacing:1.4px;
text-transform:uppercase;color:var(--mut);padding-left:3px;margin-bottom:10px}
/* Left, centre, right and each on its own tilt -- deliberately not a grid. A
   tidy column of equal cards reads as a segmented control; a loose messy stack
   reads as somebody trying real budgets one after another, which is the thing
   being demonstrated (owner 2026-08-01). Each budget also carries its own hue,
   so the cheap end and the top end are told apart by colour before the numbers
   are even read. */
.hgrid{display:flex;flex-direction:column;gap:10px}
.hbud{position:relative;display:block;min-width:0;padding:11px 14px 12px;
border-radius:var(--r);width:88%;transform-origin:50% 130%;
background:var(--card);
box-shadow:var(--sh-hair);
animation:hbmove var(--t) var(--ease) infinite both;
animation-delay:calc(var(--i) * var(--s))}
.hbud:nth-child(3n+1){align-self:flex-start}
.hbud:nth-child(3n+2){align-self:flex-end;width:96%}
.hbud:nth-child(3n+3){align-self:center;width:82%}
/* the lit state is its own layer, so a card turns on by fading one sheet in
   rather than by animating four paint properties at once */
.hbud:before{content:"";position:absolute;inset:0;border-radius:inherit;
opacity:0;background:var(--card);
box-shadow:0 2px 4px rgba(var(--rgb-ink),.06),inset 0 0 0 1.6px var(--bs);
animation:hlit var(--t) var(--ease) infinite both;
animation-delay:calc(var(--i) * var(--s))}
.hbud>*{position:relative}
/* both labels are always in the DOM, stacked in a fixed-height slot: swapping
   the text would resize the card and jog the whole grid on every turn */
.hbl{display:block;position:relative;height:13px;overflow:hidden;
font-size:9.5px;font-weight:700;letter-spacing:.6px;text-transform:uppercase}
.hbl em,.hbl u{position:absolute;left:0;top:0;font-style:normal;
text-decoration:none;white-space:nowrap;
animation-duration:var(--t);animation-timing-function:var(--ease);
animation-iteration-count:infinite;animation-fill-mode:both;
animation-delay:calc(var(--i) * var(--s))}
.hbl em{color:var(--mut);animation-name:hlabo}
.hbl u{color:var(--bc);opacity:0;animation-name:hlabi}
.hv{display:block;font-size:21px;font-weight:700;letter-spacing:-.9px;
color:var(--mut2);line-height:1.2;margin-top:2px;
animation:hvon var(--t) var(--ease) infinite both;
animation-delay:calc(var(--i) * var(--s))}
/* where this budget sits in the range, so three numbers don't have to be read
   and compared to see which is the small one */
.hbud i{display:block;height:4px;border-radius:var(--r);margin-top:9px;
background:rgba(var(--rgb-ink),.08);overflow:hidden}
.hbud i u{display:block;height:100%;border-radius:var(--r);text-decoration:none;
opacity:.28;background:var(--bc);
animation:hbaron var(--t) var(--ease) infinite both;
animation-delay:calc(var(--i) * var(--s))}
/* the connector: what the budget bought us, in one line. The arrow is drawn,
   not a "93" glyph -- the display face has no down-arrow, so the font
   stack fell through to whatever did and printed a stray symbol in the disc
   (owner 2026-08-01). */
.hfitw{position:relative;height:19px;margin:0 0 11px}
.hfit{position:absolute;left:0;top:0;display:flex;align-items:center;gap:7px;
padding-left:2px;font-size:12px;font-weight:700;color:var(--bc);opacity:0;
white-space:nowrap;
animation:hfade var(--t) var(--ease) infinite both;
animation-delay:calc(var(--i) * var(--s))}
.hfit svg{display:block;flex:none;width:18px;height:18px;padding:4px;
border-radius:var(--r);color:var(--onp);background:var(--bc)}
/* The answers are a deck, not a slideshow: the one being shown lies flat on
   top and the rest fan out behind it at their own angles, each coming forward
   in turn (owner 2026-08-01: "multiple angled result cards, carouseling on
   top of each other auto"). Photo on the LEFT, the details beside it. */
/* the fanned cards rotate around a pivot below themselves, so their corners
   swing past the flat card's box -- the bottom margin is that overhang */
.hansw{position:relative;height:150px;margin:0 0 26px}
.hans{position:absolute;left:0;right:0;top:0;display:flex;align-items:center;
gap:12px;padding:11px 14px 11px 11px;border-radius:var(--r);
opacity:0;pointer-events:none;background:var(--card);transform-origin:50% 125%;
box-shadow:var(--sh-lift);
animation:hcard var(--t) var(--ease) infinite both;
animation-delay:calc(var(--i) * var(--s))}
.hans:hover{text-decoration:none;box-shadow:var(--sh-hover)}
.hshot{display:flex;align-items:center;justify-content:center;flex:none;
width:78px;height:114px;border-radius:var(--r);background:var(--plate);
box-shadow:inset 0 1px 0 rgba(var(--rgb-white),.95)}
.hshot img{max-height:114px;width:auto;object-fit:contain;
mix-blend-mode:multiply}
.htx{display:block;min-width:0;flex:1}
.hbadge{display:inline-block;font-size:9.5px;font-weight:700;letter-spacing:.7px;
text-transform:uppercase;color:var(--onp);background:var(--teal);
padding:3px 9px;border-radius:var(--r)}
.hname{font-size:16px;font-weight:700;letter-spacing:-.4px;
color:var(--ink);line-height:1.25;margin-top:7px;
display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
overflow:hidden}
/* the price is a figure, not a field: this is where the amber earns its keep */
.hprice{display:block;font-size:19px;font-weight:700;letter-spacing:-.7px;
color:var(--ac);margin-top:5px}
.hstk{display:block;font-size:11.5px;font-weight:600;color:var(--mut);
letter-spacing:0;margin-top:3px}
.hdots{display:flex;gap:5px;justify-content:center;margin-top:13px}
.hdots i{width:5px;height:5px;border-radius:var(--r);background:rgba(var(--rgb-ink),.14);
animation:hdot var(--t) linear infinite both;
animation-delay:calc(var(--i) * var(--s))}

/* ---- start from a price ---- */
.bands{margin:0}
/* six budgets, so three across leaves two even rows -- auto-fit put five up
   and orphaned the sixth */
.bandgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
gap:10px;margin-top:12px}
.bandcard{display:flex;align-items:center;gap:12px;padding:14px 15px 13px;
border-radius:var(--r);background:var(--card);
box-shadow:var(--sh-card);
transition:transform .16s cubic-bezier(.4,0,.2,1),box-shadow .16s}
.bandcard .btx{display:flex;flex-direction:column;min-width:0;flex:1}
.bandcard .bimg{display:flex;align-items:center;justify-content:center;
flex:none;width:66px;height:88px}
.bandcard .bimg img{max-height:88px;width:auto;object-fit:contain;
mix-blend-mode:multiply}
.bandcard:hover{text-decoration:none;transform:translateY(-3px);
box-shadow:0 10px 24px rgba(var(--rgb-ink),.11),inset 0 0 0 1px var(--tint2)}
.bandcard .bl{font-size:10px;font-weight:700;letter-spacing:1.2px;
text-transform:uppercase;color:var(--mut)}
.bandcard .bv{font-size:23px;font-weight:700;letter-spacing:-.9px;color:var(--ink);
line-height:1.15;margin-top:2px}
/* the winner's name is what makes the card a doorway rather than a number */
.bandcard .bt{font-size:12.5px;font-weight:600;color:var(--mut);line-height:1.35;
margin-top:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bandcard .bn{font-size:12.5px;font-weight:700;color:var(--lnk);margin-top:8px}
.bandcard .bn i{font-style:normal;transition:transform .16s;display:inline-block}
.bandcard:hover .bn i{transform:translateX(3px)}
.grp.sm{font-size:clamp(20px,2.4vw,25px);margin-top:54px}

@media(max-width:900px){
/* Stacking put a full-width photo band above the title with the phone alone
   in the middle of it -- acres of navy either side (owner 2026-08-01: "too
   much empty space" on mobile). The photo stays BESIDE the text, just
   narrower, all the way down. */
.mfeat{grid-template-columns:minmax(0,1fr) 132px}
.mfimg{padding:14px 12px}
.mfimg img{max-height:156px}
.mgrid{grid-template-columns:1fr}
}
@media(max-width:560px){
/* No photo COLUMN on a phone: it took a third of the width and left the chip
   list so narrow that all five budgets stacked one per row (owner 2026-08-01).
   The photo is pinned beside the heading instead, short enough that the chips
   clear it, and the chips get the whole width back -- two to a row. */
.mfeat{display:block;position:relative}
.mfc{padding:17px}
/* the photo is out of flow, so the header reserves the band it occupies --
   without this the first chip row ran underneath it (owner 2026-08-01) */
.mfh{min-height:104px;padding-right:104px;align-items:center}
.mfimg{position:absolute;top:15px;right:14px;width:90px;padding:0;display:block}
.mfimg img{max-height:100px;max-width:100%;padding:5px;border-radius:var(--r)}
.mfs{margin-top:2px}
.mfchips{margin-top:14px}
.mfchips a{flex:1 1 44%;justify-content:center}
.mfchips a.all{flex:1 1 100%}
}
@media(max-width:820px){
/* On a phone the demo is the proof, so it follows the promise directly and
   the buttons close the block -- reading promise, buttons, then proof put the
   call to action before the thing that earns it (owner 2026-08-01). */
.hero.home{grid-template-columns:1fr;gap:0}
.heroc{display:contents}
.hero.home h1{order:1}
.hero.home .lead{order:2;margin-bottom:22px}
.herodemo{order:3}
.herocta{order:4;margin:26px 0 0}
.hbtn{flex:1 1 auto;justify-content:center}
}
@media(max-width:560px){
/* two ragged half-width buttons on two rows read as a mistake; full-width
   stacked reads as a choice */
.hbtn{flex:1 1 100%;justify-content:center}
/* side by side needs ~500px; below that the two columns stack back up */
.hstage{grid-template-columns:1fr;gap:14px}
/* On a phone the three budgets cascade on one spot rather than running down
   the screen -- same treatment as the answer deck below them, and it takes a
   third of the height (owner 2026-08-01). */
.hgrid{position:relative;display:block;height:80px}
.hbud,.hbud:nth-child(3n+1),.hbud:nth-child(3n+2),.hbud:nth-child(3n+3){
position:absolute;left:0;right:0;top:0;width:auto;margin:0;
padding:10px 15px 11px;border-radius:var(--r)}
.hv{font-size:19px;letter-spacing:-.7px}
.hbl{font-size:8.5px;letter-spacing:.2px}
.hfitw{margin-top:4px}
.hansw{height:140px;margin-bottom:22px}
.hans{gap:11px;padding:10px 13px 10px 10px}
.hshot{width:78px;height:106px}
.hshot img{max-height:98px}
.hname{font-size:15px;margin-top:6px}
.hprice{font-size:18px}
.bandgrid{grid-template-columns:1fr 1fr;gap:8px}
/* narrower photo + tighter chips so two budgets share a row: three stacked
   one-per-row made every category card three times taller than it needed */
.mcimg{width:62px}
.mcimg img{max-height:78px}
.mcard{gap:11px;padding:13px 14px}
.mcchips{gap:6px}
.mcchips a{padding:6px 9px;font-size:12.5px;gap:4px}
.mch h3{font-size:15.5px}
.bandcard{padding:11px 12px;gap:8px}
.bandcard .bv{font-size:19px}
.bandcard .bt{display:none}
.bandcard .bimg{width:44px;height:60px}
.bandcard .bimg img{max-height:60px}
}
/* motion off: the first budget is lit, its answer lies flat on top of the
   deck, nothing loops */
@media(prefers-reduced-motion:reduce){
.hbud,.hbud:before,.hbl em,.hbl u,.hv,.hbud i u,.hfit,.hans,.hdots i{animation:none}
.hgrid .hbud{opacity:.34;transform:rotate(var(--rot)) scale(.93)}
.hgrid .hbud:first-child{opacity:1;transform:none;z-index:5}
.hgrid .hbud:first-child:before{opacity:1}
.hfitw .hfit:first-child{opacity:1}
.hansw .hans{opacity:.42;transform:rotate(var(--rot)) translateY(15px) scale(.9)}
.hansw .hans:first-child{opacity:1;transform:none;z-index:5;pointer-events:auto}
.hdots{display:none}
.hbtn,.bandcard,.mcard,.mcchips a,.bnav a,.bpop a{transition:none}
.bnav a:hover,.bpop a:hover{transform:none}
}
/* ---- the brand section. Same reason as the device card below: it renders on
   the homepage and the /best hub now, and those load this sheet alone. Moved
   out of device_pages' _CSS_EXTRA 2026-08-07 when the brand hubs became the
   crawl path into the catalogue -- /phone/page/2..14 are noindex, so every
   device past the first hub page hung off links Google stops following.

   Rebuilt from a wrapping chip strip into a grid 2026-08-14 (owner mockup).
   The strip sized every chip to its own logo, so a 6:1 wordmark got six times
   the target of a square glyph and the row ragged differently at every width;
   an even grid gives each mark the same frame, which is also what lets the
   marks go back to the vendor's colour without reading as noise. ---- */
.bsec{margin-top:54px}
.bhead{display:flex;align-items:center;justify-content:space-between;
  gap:14px 20px;flex-wrap:wrap}
.bhead h2.grp{margin:0}
/* a filter, not a search: 26 links is past the point where scanning beats
   typing, and it costs no request -- every card is already in the page */
.bfind{position:relative;flex:0 1 330px;display:flex;align-items:center;
  color:var(--mut)}
.bfind svg{position:absolute;left:15px;pointer-events:none}
.bfind input{width:100%;font:inherit;font-size:14px;color:var(--ink);
  background:var(--card);border:1px solid var(--rule);
  border-radius:var(--r);padding:12px 14px 12px 43px;appearance:none}
.bfind input::placeholder{color:var(--mut)}
.bfind input:focus{outline:none;border-color:var(--teal);
  box-shadow:0 0 0 3px var(--tint)}
.blab{font-size:13px;font-weight:700;color:var(--mut2);margin:24px 0 10px}
.bnav{display:grid;gap:10px;
  grid-template-columns:repeat(auto-fill,minmax(196px,1fr))}
/* auto-FIT, not auto-fill: five cards spread across the full width instead of
   leaving a sixth empty track on a wide screen */
.bpop{display:grid;gap:10px;
  grid-template-columns:repeat(auto-fit,minmax(174px,1fr))}
.bpop a,.bnav a{position:relative;display:flex;align-items:center;gap:10px;
  min-width:0;font-size:13px;font-weight:700;color:var(--tx);
  background:var(--card);border:1px solid var(--rule);
  border-radius:var(--r);
  transition:border-color .16s,box-shadow .16s,transform .16s}
/* the five brands most people arrive already naming, given the size that says
   so. Mark flush left and count flush right, the SAME axis as the grid below:
   centring it read as neither centred nor aligned, because the marks are 24px
   to 122px wide and a centred mark starts in a different place in every card
   while its count stays pinned right. */
.bpop a{padding:26px 20px;min-height:106px}
/* min-height, because the marks are no longer one height: a grid row is as
   tall as its tallest card, so without a floor the rows stepped 53px, 66px,
   61px down the page against the same 10px gap */
.bnav a{padding:14px 16px;min-height:64px}
.bpop a:hover,.bnav a:hover{text-decoration:none;border-color:var(--teal);
  transform:translateY(-2px);box-shadow:var(--sh-card)}
/* the count is a separate fact from the mark; unstyled it read as part of the
   logo it sat against */
.bpop a i,.bnav a i{font-style:normal;font-size:12px;font-weight:600;
  color:var(--mut);flex:none;margin-left:auto;padding-left:8px}
/* takes the leftover space so the count keeps its own; a mark wider than what
   is left shrinks instead of running under it */
.bmk{display:flex;align-items:center;gap:10px;min-width:0;flex:1 1 auto}
.bmk b{font-size:14.5px;font-weight:600;color:var(--ink);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
/* NO height here: the height is per-file and comes from the img's own
   attribute (pages._mark_px), because one flat height sized these marks by
   the wrong dimension. max-width is the guard for a wordmark wide enough to
   push a card past a narrow screen. */
.bnav a img,.bpop a img{width:auto;max-width:100%;object-fit:contain;
  display:block;flex:0 1 auto}
/* a logo that ships its own near-black plate, flipped onto paper. Not scoped
   to the cards: a plate that disappears into the page does so on every surface
   the mark is drawn on. */
img.inv{filter:invert(1)}
/* the mark at the head of its own hub page. It sits to the RIGHT of the h1
   and the lead, not above them: stacked, a logo that spells the brand and an
   h1 that spells the brand read as the same line said twice. Off to one side
   it is what it actually is -- whose page this is -- and the h1 keeps the top
   of the page to itself. */
.bhd{display:flex;align-items:center;justify-content:space-between;gap:26px}
/* :first-child, because .blk is a div in here too and would take the grow */
.bhd>div:first-child{flex:1 1 auto;min-width:0}
.blk{flex:none;margin:0}
.blk img{width:auto;max-width:190px;object-fit:contain;display:block}
@media(max-width:640px){
.bhd{gap:14px}
/* stays on the row rather than wrapping under the h1, which would put it back
   where it was */
.blk img{max-width:96px}
}
.bnone{color:var(--mut);font-size:14.5px;margin:20px 0 0}
/* [hidden] loses to display:flex/grid, and the filter sets exactly that */
.bsec a[hidden],.bsec .bgrp[hidden]{display:none}
@media(max-width:560px){
.bnav{grid-template-columns:repeat(auto-fill,minmax(148px,1fr));gap:8px}
.bpop{grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:8px}
.bpop a{padding:20px 14px;min-height:86px}
.bnav a{padding:12px 13px}
.bfind{flex:1 1 100%}
}
/* ---- the device card: one design for the homepage teaser, the /phone
   directory and every /brand hub. It lives HERE, not in device_pages'
   _CSS_EXTRA, because the homepage loads this sheet alone. ---- */
.pgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(166px,1fr));
gap:12px;margin-top:18px}
.pcard{display:flex;flex-direction:column;border-radius:var(--r);overflow:hidden;
background:var(--card);
box-shadow:var(--sh-card);
transition:transform .16s cubic-bezier(.4,0,.2,1),box-shadow .16s}
.pcard:hover{text-decoration:none;transform:translateY(-3px);
box-shadow:0 12px 26px rgba(var(--rgb-ink),.11),inset 0 0 0 1px var(--tint2)}
.pshot{display:flex;align-items:center;justify-content:center;height:148px;
background:var(--card)}
.pshot img{max-height:130px;width:auto;object-fit:contain;
mix-blend-mode:multiply}
.pshot.noimg{color:rgba(var(--rgb-ink),.16)}
.pbod{padding:11px 13px 13px;display:flex;flex-direction:column;flex:1}
.pbr{font-size:9.5px;font-weight:700;letter-spacing:.9px;text-transform:uppercase;
color:var(--lnk)}
.pmd{font-size:14px;font-weight:600;letter-spacing:-.2px;color:var(--ink);
line-height:1.32;margin-top:3px;flex:1}
.ppr{font-size:15.5px;font-weight:700;letter-spacing:-.4px;color:var(--ink);
margin-top:9px}
/* a phone nobody stocks keeps its last price, but it must not read as one you
   can pay today: the photo steps back, the number greys out, the pill says why
   (owner 2026-08-01) */
.pcard.out .pshot{opacity:.6}
.pcard.out .ppr{color:var(--mut);font-weight:600}
.pout{display:inline-block;margin-left:6px;font-size:9px;font-weight:700;
letter-spacing:.5px;text-transform:uppercase;color:var(--lnk);
background:var(--amsoft);border-radius:var(--r);padding:2px 7px;
vertical-align:1px}
@media(max-width:560px){
.pgrid{grid-template-columns:1fr 1fr;gap:9px}
.pshot{height:124px}.pshot img{max-height:108px}
.pbod{padding:9px 10px 11px}
.pmd{font-size:13px}.ppr{font-size:14px;margin-top:7px}}

/* auto-fit, not two: on the wide shell a fixed pair of columns made each card
   half a desktop screen across */
.trend{display:grid;grid-template-columns:repeat(auto-fit,minmax(232px,1fr));
gap:11px;margin-top:14px}
.tcard{display:flex;gap:13px;align-items:center;padding:15px 16px;border-radius:var(--r);
background:var(--card);box-shadow:var(--sh-card)}
.tcard:hover{text-decoration:none;box-shadow:0 4px 14px rgba(var(--rgb-ink),.1),inset 0 0 0 1px var(--tint2)}
.tcard img{width:54px;height:72px;object-fit:contain;flex:none;
mix-blend-mode:multiply}
.tcard .tb{font-size:11.5px;font-weight:700;color:var(--lnk);letter-spacing:.6px;text-transform:uppercase}
.tcard .tn{font-size:15px;font-weight:600;color:var(--ink);line-height:1.3;margin-top:2px}
.tcard .tp{font-size:13.5px;color:var(--tx);font-weight:600;margin-top:3px}
.tcard .tp .go{color:var(--lnk);font-weight:700}
@media(max-width:560px){
.trend{grid-template-columns:1fr 1fr;gap:9px}
.tcard{flex-direction:column;align-items:flex-start;gap:9px;padding:13px 14px}
.tcard img{width:64px;height:84px}
}

.duel{display:grid;grid-template-columns:1fr 1fr;gap:11px;margin-top:16px}
.dcard{position:relative;padding:17px 18px;border-radius:var(--r);
background:var(--card);
box-shadow:var(--sh-card)}
.dcard.win{background:var(--card);box-shadow:var(--sh-lift)}
.dcard .dtop{display:flex;gap:13px;align-items:flex-start}
/* the PHONE photo, not every img in the card: unscoped, this rule was also
   catching the maker's mark in the byline beside it and drawing an Apple glyph
   as a 76x100 plate the size of the phone (owner 2026-08-15) */
.dcard .dtop>img{width:76px;height:100px;object-fit:contain;border-radius:var(--r);background:var(--bg);flex:none}
/* the badge row is reserved on BOTH cards (empty span on the loser) so the
   two columns' images/prices stay on the same baseline */
.dcard .badge{float:none;display:block;width:fit-content;margin:0 0 8px;height:24px;line-height:14px}
.dcard .badge:empty{background:none;padding-left:0}
.dcard .nm{font-family:var(--f-display);font-size:18px;font-weight:700;
letter-spacing:-.3px;line-height:1.15}
.dcard .amt{font-size:23px;font-weight:400;letter-spacing:-.8px;margin-top:9px}
.dcard .chips{margin-top:11px}
table.cmp.vs{min-width:420px}
table.cmp.vs td{white-space:normal}
.vslink{margin:12px 0 0;font-size:14px;font-weight:600}
.decide p{margin:8px 0 6px;font-size:14.5px;color:var(--ink2);line-height:1.6;max-width:76ch}
@media(max-width:560px){
.duel{grid-template-columns:1fr}
}

/* ====================== RADIUS ROLES ======================
   --r is the card. These are the two surfaces it is wrong for, named in one
   place instead of respelled at 40 call sites: things you press or read as a
   token (pill), and things small enough that 12px would eat them (sm).
   Anything absent from both lists is a card and takes --r. */
.chip,.badge,.hbadge,.pout,.cta,.hbtn,nav.linkrow a,
.mfchips a,.mcchips a,.dirnav a,
.bar .tk,.bar .ceil,.bar .dot,.hbud i,.hbud i u,.hdots i
{border-radius:var(--r-pill)}
.card .num,.st .si,.mi,.mfh .mi,.hfit svg,nav.crumbs a,
.hshot,.mcimg,.pick>img,.cmedia img,.stretch>img,.dcard .dtop>img,
.mfimg img,.pbrand img,.bigfoot .brand img
{border-radius:var(--r-sm)}

/* ================= 2026-08-15 guides, second pass =================
   What the /best/ guides draw that a device page has no concept of. Everything
   the two share -- hero, buying panel, buttons, spec tiles, rating bars, price
   chart -- lives in device_pages.SHARED_CSS and is loaded by both; the first
   pass copied those rules here under new names and they promptly drifted (a
   guide-local `.why` was overriding the device page's out-of-stock notice on
   every phone page). No new colour, radius or font: every value is a token. */
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
white-space:nowrap}
.metach{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0 0}
.metach span{font-size:12px;font-weight:600;color:var(--mut);background:var(--card);
border:1px solid var(--rule);border-radius:var(--r-pill);padding:5px 12px}

/* --- the overall score ring ---
   One arc, no script: the dash length is score/10 of the circumference. Same
   four levels as the rating bars, so a phone reads the same colour in the ring
   and in the breakdown under it. */
.ring{position:relative;flex:none;display:inline-flex;align-items:center;
justify-content:center;width:var(--rp);height:var(--rp)}
.ring svg{width:100%;height:100%;transform:rotate(-90deg)}
.ring circle{fill:none;stroke-width:4;stroke-linecap:round}
.ring .rt{stroke:rgba(var(--rgb-ink),.09)}
.ring b{position:absolute;font-family:var(--f-display);font-weight:700;
font-size:calc(var(--rp) * .34);letter-spacing:-.5px;
font-variant-numeric:tabular-nums}
.ring.l0 .rv{stroke:var(--ac)}.ring.l0 b{color:var(--lnk)}
.ring.l1 .rv{stroke:var(--aqua)}.ring.l1 b{color:var(--mut)}
.ring.l2 .rv{stroke:var(--teal)}.ring.l2 b{color:var(--teal)}
.ring.l3 .rv{stroke:var(--tealD)}.ring.l3 b{color:var(--tealD)}
.scorerow{display:flex;align-items:center;gap:13px}
.scl{display:flex;flex-direction:column;min-width:0}
.scl b{font-size:13px;font-weight:700;color:var(--ink)}
.scl i{font-style:normal;font-size:11.5px;color:var(--mut)}
/* the ratings panel's headline number: type, not a ring (owner 2026-08-15) */
.rhead{display:flex;align-items:baseline;justify-content:space-between;gap:14px;
margin-bottom:4px}
.rhead h2{margin:0}
.rov{flex:none;display:inline-flex;align-items:baseline;gap:6px;font-size:11.5px;
font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--mut)}
.rov b{font-family:var(--f-display);font-size:26px;font-weight:700;
letter-spacing:-1px;color:var(--tealD);text-transform:none;
font-variant-numeric:tabular-nums}

/* "Why it's #1": full width under the hero, one paragraph, no label column */
.whyband{display:block;margin:14px 0 0;padding:17px 20px;border-radius:var(--r);
background:var(--tint);box-shadow:inset 0 0 0 1px var(--tint2)}
.whyband .wht{display:inline-flex;align-items:center;gap:8px;
font-family:var(--f-display);font-size:12px;font-weight:700;letter-spacing:.6px;
text-transform:uppercase;color:var(--tealD)}
.whyband .wht svg{color:var(--ac)}
.whyband p{margin:8px 0 0;font-size:15px;line-height:1.62;color:var(--ink2);
max-width:96ch}

/* The guide hero IS the device hero (.dhero); `.gd` is the two things a guide
   changes about it. A guide's middle column carries a rank badge, the AI
   verdict AND the why-it-leads block, so it outruns the buying panel: the
   photo column gives width back to it, and the panel sizes to its own content
   instead of stretching into a tall empty bordered box. */
.dhero .badge{float:none;display:inline-flex;align-items:center;align-self:flex-start;
margin:0;border:1px solid var(--aqua)}
.dhero .bfor .bf{padding-top:0}
/* Desktop only. `.dhero.gd .dpick` outranks every device breakpoint rule
   (three classes to two), so an unscoped column list here also overrode the
   phone one -- the hero stayed two columns at 390px while the photo kept its
   240px width and printed straight through the text column (owner 2026-08-16).
   Above 860 the guide adds its third column; below it the device sheet is the
   only definition, which is the point of sharing the sheet. */
@media(min-width:861px){
.dhero.gd .dpick{grid-template-columns:clamp(148px,16vw,206px) minmax(0,1fr)
  clamp(272px,24vw,320px)}
}
.dhero.gd .dbuy{align-self:start}
.kspec{background:var(--card);border-radius:var(--r);padding:18px 20px;
box-shadow:var(--sh-card);margin:14px 0 0}
.kspec>h2.grp{margin:0}

/* the two evidence panels under the hero: what owners scored it, what the
   price has done. Both are device-page sections, so they only need placing. */
.panels{display:grid;grid-template-columns:1fr 1.15fr;gap:14px;
align-items:start;margin:14px 0 0}
.panels>.decide{margin:0}
@media(max-width:820px){.panels{grid-template-columns:1fr}}

/* --- the full ranking ---
   Four sortable columns, not eight: memory, display and the shop count moved
   under the phone's name where they wrap, which is what took the table off a
   sideways scrollbar at desktop width (owner 2026-08-15). */
table.rank{width:100%;border-collapse:collapse;font-size:13.5px}
table.rank th{text-align:left;font-size:11px;font-weight:700;letter-spacing:.4px;
text-transform:uppercase;color:var(--mut);padding:16px 14px 11px;white-space:nowrap;
border-bottom:1px solid var(--rule)}
table.rank td{padding:13px 14px;border-bottom:1px solid var(--rule);color:var(--tx);
vertical-align:middle}
table.rank tbody tr:last-child td{border-bottom:none}
table.rank tbody tr:hover td{background:rgba(var(--rgb-teal),.04)}
.rrk{width:34px}
.rrk span{display:inline-flex;align-items:center;justify-content:center;width:25px;height:25px;
border-radius:var(--r-sm);background:rgba(var(--rgb-ink),.06);color:var(--mut);font-weight:700;font-size:12.5px}
.rrk span.top{background:var(--teal);color:var(--onp)}
.rph{display:flex;align-items:center;gap:12px;min-width:210px}
.rph img,.rth{width:44px;height:58px;flex:none;object-fit:contain;background:var(--bg);border-radius:var(--r-sm)}
.rnm{display:flex;flex-direction:column;gap:2px;min-width:0}
.rnm a,.rnm>span{font-size:14.5px;font-weight:700;color:var(--ink);letter-spacing:-.2px}
.rnm a:hover{color:var(--lnk)}
.rnm i{font-style:normal;font-size:12px;color:var(--mut);line-height:1.35}
.rnm u{font-size:11.5px;color:var(--teal);font-weight:600;text-decoration:none}
.rsc{width:56px}
.rpr{font-size:15px;font-weight:700;color:var(--ink);white-space:nowrap;
font-variant-numeric:tabular-nums}
.rhi{display:block;font-size:12px;font-weight:500;color:var(--mut)}
/* no min-width on the spec cells: a floor on every column is what pushed the
   table past its own container and cut the last one off (owner 2026-08-15).
   They wrap instead, and the two that cannot usefully wrap leave on narrow
   screens. */
.rcs,.rbt{font-size:13px;line-height:1.4}
.rns{color:var(--faint)}
.rgoc{width:1%;text-align:right}
.rgo{white-space:nowrap;padding:9px 15px}
/* the container is rounded, so a cell flush to its edge reads as cut off
   (owner 2026-08-15: "did u fix the padding in tables ... so it dont look
   cut") -- the first and last column keep clear of the corner radius */
table.rank th:first-child,table.rank td:first-child{padding-left:18px}
table.rank th:last-child,table.rank td:last-child{padding-right:18px}
@media(max-width:1000px){.rcs{display:none}}
@media(max-width:820px){.rbt{display:none}}
/* phone-width: nothing is dropped, everything shrinks. The button keeps its
   arrow and loses only the word, the ring loses 8px, the cells lose their
   desktop breathing room -- and the whole table fits 360px with no scroll. */
@media(max-width:560px){
.rph{min-width:0;gap:9px}
table.rank{font-size:12.5px}
table.rank td{padding:10px 7px}
table.rank th{padding:13px 7px 9px}
table.rank th:first-child,table.rank td:first-child{padding-left:12px}
table.rank th:last-child,table.rank td:last-child{padding-right:12px}
.rrk{width:26px}
.rrk span{width:21px;height:21px;font-size:11.5px}
.rph img,.rth{width:36px;height:48px}
.rnm a,.rnm>span{font-size:13px}
.rnm u{font-size:11px}
/* the memory+display line wrapped to five lines in a 165px column and made
   every row four times taller than it needed to be -- the cards above carry
   the same two figures, so the phone-width table keeps name and stock only */
.rnm i{display:none}
.rsc{width:36px}
.rsc .ring{width:34px;height:34px}
.rsc .ring b{font-size:11.5px}
.rpr{font-size:13.5px}
.rgo{padding:8px 10px;gap:0}
.rgo b{display:none}
}
@media(max-width:400px){.rsc{display:none}}

/* --- "which one should you buy": a real grid of small square-ish tiles ---
   It was a bullet list whose only hit area was the words "full list", then a
   row of wide cards that stacked into four tall banners on a phone (owner
   2026-08-15, twice). auto-fit at 150px runs four across a desktop and TWO
   across a phone, and the cell is compact enough to stay roughly square at
   both: photo, what it wins, which phone, the way through. */
.dgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
gap:10px;margin:16px 0 6px}
.dcell{display:flex;flex-direction:column;align-items:center;text-align:center;
gap:7px;padding:15px 13px 13px;border-radius:var(--r);
background:var(--card);box-shadow:var(--sh-card);
transition:box-shadow .16s,transform .16s}
a.dcell:hover{text-decoration:none;box-shadow:var(--sh-hover);transform:translateY(-2px)}
.dcell img,.dcell .rth{width:52px;height:68px;flex:none;object-fit:contain}
.dcell .di{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;
gap:2px 6px;font-size:11px;font-weight:700;
letter-spacing:.4px;text-transform:uppercase;color:var(--teal)}
.dcell .di svg{flex:none}
.dcell b{font-family:var(--f-display);font-size:13.5px;font-weight:700;
letter-spacing:-.2px;color:var(--ink);line-height:1.25}
.dcell .dgo{margin-top:auto;padding:8px 13px;font-size:12px}
.dcell.now{background:var(--tint);box-shadow:inset 0 0 0 1px var(--tint2)}
.dcell.now .dgo{background:none;color:var(--tealD);padding-left:0;padding-right:0}
@media(max-width:420px){.dcell img,.dcell .rth{width:44px;height:58px}}

/* --- the phone-only shortcut ---
   A pill, not a bar: it is a shortcut to a link the page already contains, so
   it is sized like one, it is not painted until the hero has gone, and it
   retires while the real picker card is on screen. */
.mcta{display:none}
@media(max-width:720px){
.mcta{display:inline-flex;align-items:center;gap:7px;position:fixed;
left:50%;bottom:calc(14px + env(safe-area-inset-bottom));z-index:70;
padding:11px 20px;border-radius:var(--r-pill);
background:var(--teal);color:var(--onp);font-weight:700;font-size:14px;
box-shadow:0 8px 22px -8px rgba(var(--rgb-ink),.55),
  inset 0 1px 0 rgba(var(--rgb-white),.2);
opacity:0;pointer-events:none;
transform:translate(-50%,140%) scale(.94);
transition:transform .34s cubic-bezier(.22,1,.36,1),opacity .22s ease}
.mcta.on{opacity:1;pointer-events:auto;transform:translate(-50%,0) scale(1)}
.mcta i{font-style:normal;transition:transform .16s}
.mcta:active i{transform:translateX(3px)}
.mcta:hover{text-decoration:none;background:var(--tealD)}
main.wrap{padding-bottom:76px}}
@media(prefers-reduced-motion:reduce){.mcta{transition:opacity .2s ease}
.mcta,.mcta.on{transform:translate(-50%,0)}}

/* ===================== GLASS SURFACES =====================
   Last in the sheet, because this restates the fill of surfaces defined above.
   TWO tiers, and the split is a performance decision, not a visual one: a
   backdrop-filter is a full-surface GPU pass, and a guide page carrying one
   per card would ship fourteen of them to the mid-range Androids that are most
   of this site's traffic. So the few large surfaces blur what is behind them,
   and the many small repeated ones take the translucent fill and the rim
   without a filter -- over a soft ground the difference is a highlight, and
   over a scrolling one only the big panels are large enough for the blur to
   read at all. */
.hero,.kspec,.decide,.tblwrap,.specdump>summary{
background:var(--glass-solid);
backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
box-shadow:var(--sh-glass)}
.card,.stretch,.dcell,.tcard,.pcard,.ccard,.mfcard{
background:var(--glass-solid);box-shadow:var(--sh-glass)}
a.dcell:hover,a.stretchcard:hover .stretch,.tcard:hover,.pcard:hover{
box-shadow:var(--sh-glass-hover)}
/* the dispersion edge, on the two surfaces big enough to own one: a real edge
   splits entering light, and masked to 1px this is the logo's own material
   rather than a decoration (the header has drawn it since the reskin). */
.hero::before,.pickcard::before{content:"";position:absolute;left:14px;right:14px;
top:0;height:1px;pointer-events:none;background:var(--prism);opacity:.85;
border-radius:var(--r-pill)}
.pickcard{position:relative;overflow:hidden}
/* a chip is a token, not a pane: it keeps a flat tinted fill, or a row of
   them turns into fifteen little windows */
@media(prefers-reduced-transparency:reduce){
.hero,.kspec,.decide,.tblwrap,.card,.stretch,.dcell,.specdump>summary{
background:var(--card);backdrop-filter:none;-webkit-backdrop-filter:none}}

/* budget links as a grid, and worth-stretching links through to the phone */
nav.linkrow{display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));gap:8px}
a.stretchcard,a.stretchcard:hover{text-decoration:none}
a.stretchcard .stretch{transition:box-shadow .16s}
a.stretchcard:hover .stretch{box-shadow:var(--sh-hover)}
.stretch .scv{flex:none;font-size:22px;line-height:1;color:var(--mut2)}

/* price history */
/* stat chips, colour-coded: lowest green, highest amber (owner 2026-07-25) */
.ph .phstat{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0 8px}
.ph .phstat span{display:flex;flex-direction:column;gap:1px;font-size:10px;
  text-transform:uppercase;letter-spacing:.6px;font-weight:700;
  border-radius:var(--r);padding:8px 13px;background:rgba(var(--rgb-ink),.045);
  color:var(--mut)}
.ph .phstat b{font-family:var(--f-price);font-variant-numeric:tabular-nums;font-size:16px;font-weight:700;
  letter-spacing:-.4px;color:var(--ink);text-transform:none;white-space:nowrap}
.ph .phstat .s0{background:var(--amsoft);color:var(--lnk)}
.ph .phstat .s0 b{color:var(--lnk)}
.ph .phstat .s1{background:var(--tealL);color:var(--tealD)}
.ph .phstat .s1 b{color:var(--tealD)}
.ph .phstat .s2{background:var(--tone);color:var(--mut)}
.ph .phstat .s2 b{color:var(--ink2)}
@media(max-width:720px){
/* all four on ONE row on a phone (owner 2026-07-25): a grid, not wrapping
   flex, so "days tracked" can never drop to a second line */
.ph .phstat{display:grid;grid-template-columns:repeat(4,1fr);gap:5px}
.ph .phstat span{padding:7px 6px;font-size:8.5px;letter-spacing:.3px;
  align-items:center;text-align:center}
.ph .phstat b{font-size:12.5px;letter-spacing:-.5px}}
@media(max-width:360px){.ph .phstat b{font-size:11.5px}
.ph .phstat span{padding:6px 3px}}
/* Owner 2026-08-01: "price history looks bad in desktop". It was a 640px card
   marooned in a 980px shell, holding a 96px-tall line stretched sideways with
   preserveAspectRatio:none -- so the curve flattened, the fill sagged and the
   whole thing read as a decoration. Full width, real height, real gridlines
   with the prices on them, and a chart that keeps its own proportions. */
.ph{max-width:none}
.ph .phstat{gap:9px}
/* the gutter on the right belongs to the gridline prices, which are real HTML
   at a real font size -- inside the SVG they would be stretched by the same
   preserveAspectRatio:none the plot geometry needs */
.ph .spark{position:relative;margin:16px 0 0;padding-right:92px}
.ph .plot{position:relative;height:clamp(150px,20vw,236px)}
.ph .plot svg{width:100%;height:100%;display:block;overflow:visible}
.ph .gl{stroke:rgba(var(--rgb-ink),.085);stroke-width:1;
  vector-effect:non-scaling-stroke}
.ph .glabs{position:absolute;right:0;top:0;height:clamp(150px,20vw,236px);
  width:86px;pointer-events:none}
.ph .glabs span{position:absolute;right:0;transform:translateY(-50%);
  font-size:12px;font-weight:600;color:var(--mut);white-space:nowrap}
.ph .pdot{position:absolute;width:11px;height:11px;border-radius:var(--r);
  transform:translate(-50%,-50%);background:var(--dc,var(--ac));
  box-shadow:0 0 0 3px var(--card)}
/* Which line is which. Only drawn when there are two of them -- a legend for
   one series is a label for a thing already labelled four ways above it. */
.ph .phlegend{display:flex;flex-wrap:wrap;gap:7px 16px;margin:2px 0 -4px}
.ph .phkey{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;
  font-weight:600;color:var(--mut);font-variant-numeric:tabular-nums}
.ph .phkey::before{content:"";width:14px;height:3px;border-radius:2px;
  background:var(--dc)}
.ph .phax{display:flex;justify-content:space-between;font-size:12px;
  color:var(--mut);margin-top:10px;padding:0 2px}
@media(max-width:600px){
.ph .spark{padding-right:74px}
.ph .glabs{width:70px}
.ph .glabs span{font-size:11px}}
.rrow{display:grid;grid-template-columns:104px 1fr 32px;gap:8px 12px;align-items:center;
  font-size:13.5px;color:var(--tx);padding:5px 0}
.rrow .rbar{height:7px;border-radius:var(--r);background:rgba(var(--rgb-ink),.08);overflow:hidden}
.rrow .rbar i{display:block;height:100%;border-radius:var(--r)}
.rrow b{font-weight:600;text-align:right}
/* the bar earns its colour: weak scores stay pale and warm, strong ones go
   deep teal, so the shape of a phone's strengths reads at a glance */
.rrow.l0 .rbar i{background:linear-gradient(90deg,var(--ac),var(--lnk))}
.rrow.l1 .rbar i{background:linear-gradient(90deg,var(--aquaL),var(--aqua))}
.rrow.l2 .rbar i{background:linear-gradient(90deg,var(--aqua),var(--teal))}
.rrow.l3 .rbar i{background:linear-gradient(90deg,var(--teal),var(--tealD))}
.rrow.l0 b{color:var(--lnk)}
.rrow.l1 b{color:var(--mut)}
.rrow.l2 b{color:var(--teal)}
.rrow.l3 b{color:var(--tealD)}
.spark{margin:10px 0 2px}
.spark svg{max-width:100%;height:auto;display:block}
.sold{opacity:.5}
@media(max-width:640px){.rrow{grid-template-columns:88px 1fr 30px}}
.ic{flex:none;vertical-align:-3px}
/* hero: the photo owns the whole left column, everything else stacks right */
.dhero{position:relative}
/* The photo column was capped at 168px and the tile stretched to whatever
   height the text happened to be, so on a desktop the hero was a small phone
   floating in a tall white box beside a squeezed text column (owner
   2026-08-01). It is aspect-locked and sized off the viewport now, and the
   text column is what fills the remaining width. */
.dhero .dpick{display:grid;grid-template-columns:clamp(140px,19vw,236px) 1fr;
  gap:clamp(16px,3vw,36px);align-items:center}
.dimg{display:flex;align-items:center;justify-content:center;background:var(--plate);
  border-radius:var(--r);padding:14px;aspect-ratio:3/4;
  box-shadow:var(--sh-hair),inset 0 1px 0 rgba(var(--rgb-white),.95)}
.dimg img{width:100%;height:100%;object-fit:contain;display:block}
.dhero .top{display:flex;flex-direction:column;justify-content:center;min-width:0}
.dhero .brand{font-size:14px}
.dhero .top h2{font-size:clamp(22px,3.6vw,34px);margin:2px 0 4px;
  letter-spacing:-.9px;line-height:1.1}
/* one line, always: a range broken over two lines read as two prices */
.dhero .price{margin-top:14px;align-items:baseline;gap:12px}
.dhero .price .amt{white-space:nowrap;font-size:clamp(26px,3.4vw,40px)}
/* out of stock: the number is the last price we saw, not one you can pay
   today, so it stops looking like a live price and says why (owner
   2026-08-01 -- an iPhone 14 at a stale high price read as current) */
.dhero .price.oos .amt{color:var(--mut);font-weight:400}
.pgone{font-size:11.5px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;
  color:var(--lnk);background:var(--amsoft);border-radius:var(--r);padding:4px 11px;
  white-space:nowrap}
/* the price we cannot verify sits in the corner, out of the reading flow */
.unconf.corner{position:absolute;top:12px;right:14px;font-size:10.5px;
  font-weight:700;letter-spacing:.3px;text-transform:uppercase;
  background:var(--amsoft);color:var(--lnk);border-radius:var(--r);
  padding:4px 10px;white-space:nowrap}
@media(max-width:640px){
/* the tile used to stretch to the full text height, leaving the photo floating
   in a tall empty box on a phone (owner, Pixel 5) */
.dhero .dpick{grid-template-columns:104px 1fr;gap:13px;align-items:start}
.dimg{padding:7px;border-radius:var(--r)}
.dhero .top h2{font-size:21px;letter-spacing:-.5px}
.dhero .price{margin-top:11px}
.unconf.corner{position:static;display:inline-block;margin-bottom:8px}
}
/* headline specs as tiles, not a middot-run (owner 2026-07-25) */
.kfx{display:flex;flex-wrap:wrap;gap:9px 20px;margin:16px 0 2px;padding-top:14px;
  border-top:1px solid var(--rule)}
.kft{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;font-weight:600;
  color:var(--tx)}
.kft .ic{color:var(--lnk);opacity:.85}
/* ---- device page, 2026-08-14 ----
   Three columns: what it looks like, what it is, what it costs and the button
   that acts on that. The price used to sit in the middle column with the
   shops it refers to four sections below it. */
/* stretch, not start: the buying panel is the tallest of the three, so with
   start-aligned columns the middle one stopped at its last chip and left a
   band of white under it for the panel's whole remaining height. The columns
   share a height now and the middle one spreads into it -- the chips fall to
   the bottom, level with the panel's foot. The photo keeps its own aspect
   ratio and opts out. */
.dhero .dpick{grid-template-columns:clamp(180px,23vw,300px) minmax(0,1fr)
  clamp(280px,25vw,330px);align-items:stretch}
.dhero .dleft{align-self:start;min-width:0}
/* The photo is the first thing on the page and it was a 215px thumbnail in a
   bordered plate, which is what made the hero read as cramped next to the
   reference. Big, and on the card's own paper -- the plate wash and hairline
   were drawing a box around a picture that already has its own edges. */
.dhero .dimg{background:none;box-shadow:none;padding:0;width:100%;
  aspect-ratio:3/4}
.dhero .dimg img{width:100%;height:100%;object-fit:contain}
.dhero .dleft .chips{margin:12px 0 0}
.dhero .dleft .chip{font-size:11.5px;padding:4px 10px}
.dhero .top h2{font-size:clamp(28px,4.2vw,44px);letter-spacing:-1.4px;
  margin:1px 0 0}
.dhero .brand{font-size:14.5px;color:var(--mut);font-weight:600}
/* value over label: a row of bare figures had no word for what each one is */
.kfx{gap:14px 26px}
.kft{align-items:flex-start;gap:9px}
.kft .kv{display:flex;flex-direction:column;line-height:1.25}
.kft .kv b{font-size:14px;font-weight:700;color:var(--ink);letter-spacing:-.2px}
.kft .kv i{font-style:normal;font-size:11.5px;font-weight:500;color:var(--mut)}
/* the variant control */
/* Label ABOVE the control, not beside it. Side by side the select got
   whatever the label left, and the option it has to show is now a config, a
   channel and a price -- "4GB/128GB - Import - BDT19,799" was clipped to
   "4GB/128GB - BDT19,49" in a 320px panel, which is worse than no label at
   all: a truncated price reads as a real one. */
.dbpick{display:flex;flex-direction:column;align-items:stretch;gap:7px;
  margin-top:15px;padding-top:13px;border-top:1px solid var(--rule)}
.dbpick label{font-size:12.5px;font-weight:600;color:var(--mut)}
/* our chevron, not the platform's: the OS arrow sits in its own grey gutter
   and made the control the one piece of foreign UI in the panel. The open
   list is drawn by the OS and cannot be styled -- that is the price of a
   native select, and it is worth paying for the keyboard and screen-reader
   behaviour a div-based dropdown would have to reimplement. */
.dbpick select{-webkit-appearance:none;appearance:none;min-width:0;flex:1;
  font:inherit;font-size:13px;font-weight:700;color:var(--ink);
  background:var(--card);box-shadow:inset 0 0 0 1px var(--rule);border:0;
  border-radius:var(--r-sm);padding:10px 32px 10px 12px;cursor:pointer;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%2363726f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6.5 4 4 4-4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center}
.dbpick select:hover{box-shadow:inset 0 0 0 1px var(--teal)}
.dbpick select:focus-visible{outline:2px solid var(--teal);outline-offset:1px}
.dbnote{margin:13px 0 0;font-size:11.5px;line-height:1.5;color:var(--mut)}
.dhero .top{justify-content:flex-start}
.dhero .top .chips{margin-top:14px}
/* the three-line answer, inside the hero's middle column. Label and point on
   ONE row: stacked, three points cost six lines and the uppercase label
   shouted over the sentence it was introducing. */
.bfor{display:block;margin:0;background:none;box-shadow:none;border-radius:0}
.dhero .bfor{margin-top:auto;padding-top:15px;
  border-top:1px solid var(--rule)}
/* 104px, and aligned to the TOP not the baseline: "ALTERNATIVE" and "WATCH
   OUT FOR" do not fit 86px, so they wrapped and then baseline-aligned their
   LAST line against the first line of the point, which is what made the two
   columns collide (owner 2026-08-14). */
.bf{display:grid;grid-template-columns:minmax(0,104px) minmax(0,1fr);
  align-items:start;gap:4px 14px;padding:9px 0}
.bf+.bf{box-shadow:none;border-top:1px solid var(--rule)}
.bf dt{display:flex;align-items:center;gap:7px;font-size:10.5px;font-weight:700;
  line-height:1.35;letter-spacing:.6px;text-transform:uppercase;
  color:var(--teal)}
.bf.wn dt{color:var(--ac)}
.bf.al dt{color:var(--mut)}
.bf .bi{flex:none;display:flex}
.bf dd{margin:0;font-size:13.5px;line-height:1.45;color:var(--ink2)}
.balt{display:inline-flex;align-items:center;gap:8px;color:var(--lnk);
  font-weight:600}
/* the label centres against this row only: it is the one row whose value is
   an image-height object rather than a line of text */
.bf.al dt{align-self:center}
.balt:hover{text-decoration:none}
.balt:hover span{text-decoration:underline}
/* no plate and no hairline: a 26px cutout does not need a box drawn round it,
   and the negative margin keeps the taller image from inflating the row so
   this line sits on the same rhythm as the two above it */
.balt img{flex:none;width:auto;height:30px;object-fit:contain;padding:0;
  border-radius:0;background:none;box-shadow:none;margin:-6px 0}
.balt i{font-style:normal;flex:none;transition:transform .16s
  cubic-bezier(.4,0,.2,1)}
.balt:hover i,.balt:focus-visible i{transform:translateX(4px)}
@media(prefers-reduced-motion:reduce){.balt i{transition:none}}
@media(max-width:520px){.bf{grid-template-columns:1fr;gap:2px}}
/* one spacing step between hero blocks instead of five different ones */
.dhero .top>*+*{margin-top:14px}
.dhero .top .kfx{margin-top:18px;padding-top:16px}
.dhero .top .bfor{margin-top:16px}
.dchip{display:inline-flex;align-items:center;gap:8px;margin-top:11px;
  font-size:13.5px;font-weight:700;color:var(--tealD);background:var(--tealL);
  border-radius:var(--r-pill);padding:8px 15px}
.dsum{margin:13px 0 0;font-size:15.5px;line-height:1.55;color:var(--tx);
  max-width:52ch}
/* the buying panel. It sits INSIDE a glass hero, so it takes the denser glass
   fill rather than flat white: white-on-glass reads as a patch, and the panel
   is where the money is -- it should look like the front pane, not a sticker. */
.dbuy{border-radius:var(--r);background:var(--glass-solid);
  box-shadow:var(--sh-glass);padding:20px 20px 18px}
/* -1.2px was tuned for Franklin's figures and crushed the comma out of a
   five-digit taka amount at 32px. Anek's are wider: less tracking, tabular so
   the two halves of a range line up under each other when the panel wraps. */
.dbamt{font-family:var(--f-price);font-size:clamp(24px,2.6vw,32px);
  font-weight:700;letter-spacing:-.3px;line-height:1.15;color:var(--ac);
  font-variant-numeric:tabular-nums}
.dbamt .dash{color:var(--faint);font-weight:400}
.dbamt.oos{color:var(--mut)}
.dbmeta{margin-top:7px;font-size:12.5px;color:var(--mut)}
/* hairline rows, not a stack of grey slabs: five filled blocks in a column
   read as five buttons that do nothing (owner 2026-08-14) */
.dbrows{margin:15px 0 0;display:flex;flex-direction:column;
  border-top:1px solid var(--rule)}
.dbrows>div{display:flex;align-items:baseline;justify-content:space-between;
  gap:12px;padding:10px 1px;border-bottom:1px solid var(--rule)}
.dbrows dt{flex:none;font-size:12.5px;font-weight:600;color:var(--mut)}
.dbrows dd{margin:0;text-align:right;font-size:13.5px;font-weight:700;
  color:var(--ink);font-variant-numeric:tabular-nums}
.dbuy .dbtn.wide{display:flex;justify-content:center;width:100%;margin-top:14px;
  font-size:14.5px;padding:14px 18px;border-radius:var(--r-sm)}
/* three rows, icon beside the words. As three stacked columns the icon got a
   full-width box of its own above its label and read as an empty outlined
   tile with some text under it (owner 2026-08-14). */
.dbtrust{list-style:none;margin:14px 0 0;padding:13px 0 0;
  border-top:1px solid var(--rule);display:flex;flex-direction:column;gap:10px}
.dbtrust li{display:flex;align-items:center;gap:10px}
.dbtrust .ti{flex:none;display:flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:50%;background:var(--tealL);
  color:var(--tealD)}
.dbtrust .tt{min-width:0}
.dbtrust b{display:block;font-size:12px;font-weight:700;line-height:1.3;
  color:var(--ink)}
.dbtrust i{font-style:normal;display:block;font-size:11px;line-height:1.3;
  color:var(--mut)}
/* best for / watch out / alternative */
/* the old three-across card version of .bfor lived here and, being LATER in
   the sheet than the rewrite above, quietly won: the hero rendered the points
   as boxed columns with a 132px label squeezed to three lines. One
   definition, and it is the one above. */
@media(max-width:860px){
.dhero .dpick{grid-template-columns:clamp(130px,20vw,190px) minmax(0,1fr)}
.dbuy{grid-column:1/-1;margin-top:16px}
}
/* key specs as tiles */
.ksgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:9px;margin-top:14px}
.kst{display:flex;gap:10px;padding:12px 13px;border-radius:var(--r-sm);
  box-shadow:var(--sh-hair)}
.kst .ki{flex:none;color:var(--teal)}
.kst b{display:block;font-size:12.5px;font-weight:700;color:var(--mut);
  letter-spacing:.1px}
.kst i{font-style:normal;display:block;margin-top:2px;font-size:13.5px;
  font-weight:700;color:var(--ink);line-height:1.3}
.ksmore{display:inline-block;margin-top:14px;font-size:13px;font-weight:700;
  color:var(--lnk)}
.ksmore i{font-style:normal;display:inline-block;transition:transform .16s}
.ksmore:hover i{transform:translateX(3px)}
.dbtn{display:inline-flex;align-items:center;gap:8px;font-size:13.5px;
  font-weight:700;color:var(--onp);background:var(--teal);
  border-radius:var(--r-pill);padding:11px 19px;
  box-shadow:inset 0 1px 0 rgba(var(--rgb-white),.18)}
.dbtn:hover{text-decoration:none;background:var(--tealD)}
.dbtn i{font-style:normal;display:inline-block;transition:transform .16s}
.dbtn:hover i{transform:translateY(2px)}
.dbtn.ghost{color:var(--lnk);background:var(--card);
  box-shadow:inset 0 0 0 1px var(--rule)}
.dbtn.ghost:hover{background:var(--card);box-shadow:inset 0 0 0 1px var(--teal)}
.dbtn.ghost:hover i{transform:translateX(3px)}
@media(max-width:560px){.dact{gap:7px}.dbtn{font-size:12.5px;padding:10px 14px}}
/* ---- phones. The device hero was 130px of photo beside a 160px text column
   at 375px: two cramped columns pretending to be a layout. It stacks. ---- */
@media(max-width:640px){
.dhero .dpick{grid-template-columns:1fr;gap:18px}
/* .dhero .dimg, not .dimg: the desktop rule is two classes deep, so a
   single-class override here loses the cascade and the photo stays full size */
.dhero .dimg{width:min(62vw,240px);margin:0 auto;padding:0}
.dhero .dleft .chips{justify-content:center}
/* 2x2, not three-then-one: four tiles wrapping by content width left an
   orphan on its own row and the column edges never lined up */
.kfx{display:grid;grid-template-columns:1fr 1fr;gap:13px 14px}
.dhero .bfor{margin-top:0}
/* Inline on a phone, not stacked and not columned. A 96px label column leaves
   the point 200px to live in; stacking gives each point two lines and three
   points six. Run the label into the sentence and it is three lines with no
   column to misalign. */
.bf{display:block;padding:8px 0}
.bf dt{display:inline-flex;vertical-align:baseline;margin-right:8px;
  font-size:10px}
.bf dd{display:inline}
.dhero .top h2{font-size:26px;letter-spacing:-.7px}
.dchip{font-size:12.5px;padding:7px 12px;margin-top:10px}
.dsum{font-size:15px;margin-top:11px}
.dbuy{grid-column:1/-1;margin-top:0;padding:16px}
.dbamt{font-size:27px}
.dbuy .dbtn.wide{padding:13px 16px;font-size:14px}
/* three 83px columns of 10px type is not a row of reassurance, it is three
   clipped words -- one per line, icon beside the text */
.ksgrid{grid-template-columns:1fr 1fr;gap:7px}
.kst{flex-direction:column;gap:7px;padding:11px}
}
/* 360, not 400: at 375 -- the commonest phone width there is -- two spec
   tiles fit and one wastes half the row */
@media(max-width:360px){
.ksgrid{grid-template-columns:1fr}
.kst{flex-direction:row;gap:10px}
}

/* --- the full GSMArena dump (owner 2026-07-26: no simplification) ---------
   A band list is 120 characters of commas and the .cmp table is nowrap with a
   600px floor, so reusing it would have put every phone page into sideways
   scroll on a phone. This is its own grid: a fixed label column that collapses
   to a stacked label-above-value on mobile, and values that break anywhere so
   nothing can push the page wider than the screen. */
/* --- the shop board --------------------------------------------------------
   A grouped filter panel over one grid of listing cards, replacing the
   four-column table (owner 2026-08-04, off a Claude Design handoff). Three
   things the table could not do:

   * every filter option carries the count it would return, so a buyer can see
     "Official 3" before spending a click to find out;
   * the cheapest listing anyone can actually pay today gets its own dark band
     above the grid, with a sentence saying what it beats -- or that something
     cheaper exists but is sold out, which an in-stock-first sort used to hide
     by simply reordering;
   * each card carries what it costs OVER that cheapest one, so the spread is
     readable without arithmetic.

   The handoff put the filters in a left rail. That shape does not survive our
   data: an iPhone carries five axes and twenty-three options, and the rail
   measured 1138px against a 540px board -- a filter taller than the thing it
   filters. So the groups run across the top and wrap like text, which is also
   the one layout that cannot clip on a phone (owner 2026-08-02).

   Hidden until the script runs, so with JS off the page is still a complete
   price list rather than a widget that does nothing. */
.shopboard{display:flex;flex-direction:column;gap:13px;margin:18px 0 0}
.ofilt{display:flex;flex-wrap:wrap;align-items:flex-start;gap:13px 24px;
  padding:14px 16px;border-radius:var(--r);background:var(--card);
  box-shadow:var(--sh-hair)}
/* a phone with no axis worth filtering on still ships the node (the script
   keys off it), and an empty panel would otherwise draw its own hairline */
.ofilt[hidden],.ofilt:empty{display:none}
.fgrp{display:flex;flex-direction:column;gap:6px;min-width:0}
.fhd{display:flex;align-items:baseline;gap:7px}
.flab{font-size:10.5px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;
  color:var(--mut)}
.fhint{font-size:10.5px;color:var(--mut2)}
.fset{display:flex;flex-wrap:wrap;gap:5px;min-width:0}
.fb{display:inline-flex;align-items:center;gap:8px;min-height:34px;font:inherit;
  font-size:12.5px;font-weight:600;cursor:pointer;padding:6px 12px;
  border-radius:var(--r);color:var(--tx);background:var(--card);
  border:1px solid var(--rule);white-space:nowrap;
  transition:color .14s,background .14s,border-color .14s}
/* A long axis is capped rather than run across three lines. An iPhone lists
   seven import markets; five plus "All", then the rest on request. */
.fset.cap .fb:nth-child(n+7){display:none}
.fmoreg{align-self:flex-start;font:inherit;font-size:11.5px;font-weight:700;
  color:var(--lnk);background:none;border:0;padding:2px 2px;cursor:pointer;
  text-decoration:underline;text-underline-offset:3px}
.fmoreg[hidden]{display:none}
.fb:hover:not(:disabled){color:var(--lnk);border-color:var(--teal)}
.fb:focus-visible{outline:2px solid var(--teal);outline-offset:1px}
.fb.on{color:var(--tealD);font-weight:700;background:var(--tealL);
  border-color:var(--teal)}
/* an option with nothing behind it is dimmed, never removed: a button row that
   reshuffles under the cursor is impossible to aim at */
.fb:disabled{color:var(--faint);cursor:default;background:var(--tone);
  border-color:transparent}
.fbl{min-width:0;overflow:hidden;text-overflow:ellipsis}
/* the count is the point: it turns every option into a preview of its result */
.fc{flex:none;font-size:10.5px;font-weight:600;font-style:normal;color:var(--mut2);
  font-variant-numeric:tabular-nums}
.fb.on .fc{color:var(--lnk)}
.fb:disabled .fc{color:var(--faint)}
/* the swatch IS the colour, so a paint we could not map hatches rather than
   guessing at a hex (owner 2026-08-02) */
.fsw{flex:none;width:14px;height:14px;border-radius:50%;
  background:var(--paint,repeating-linear-gradient(45deg,var(--rule) 0 3px,
    var(--bg) 3px 6px));box-shadow:inset 0 0 0 1px rgba(var(--rgb-ink),.22)}
.fmore{display:none}
.freset{margin-left:auto;align-self:center;font:inherit;font-size:12px;
  font-weight:600;color:var(--mut);background:none;border:0;padding:4px 2px;
  cursor:pointer;text-decoration:underline;text-underline-offset:3px}
.freset:hover{color:var(--lnk)}

/* --- the board ------------------------------------------------------------- */
.oboard{min-width:0;display:flex;flex-direction:column;gap:11px}
.ohead{display:flex;align-items:baseline;justify-content:space-between;gap:16px;
  flex-wrap:wrap}
/* what the current selection actually holds, rewritten on every click */
.osum{font-size:13.5px;color:var(--tx)}
.osum b{font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.osort{font-size:11px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;
  color:var(--mut2)}
.osum[hidden],.obest[hidden],.oempty[hidden]{display:none}
/* the warranty premium, subtracted for the reader and told once */
.owarn{margin:14px 0 0;font-size:13.5px;line-height:1.55;color:var(--mut)}
.owarn b{color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums}

/* --- the cheapest you can actually pay -------------------------------------
   The one figure this whole page exists to produce, so it gets the dark panel.
   The price on it is white, not amber: in the logo the amber mark never
   touches a dark field, and amber-on-dark was tried and removed (brand SPEC). */
.obest{display:flex;align-items:center;justify-content:space-between;
  gap:14px 22px;flex-wrap:wrap;padding:15px 18px;border-radius:var(--r);
  background:var(--panel);color:var(--onp);box-shadow:var(--cta-dark-sh)}
.obwho{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 200px}
.obn{display:flex;flex-direction:column;gap:3px;min-width:0}
.obn b{font-size:16.5px;font-weight:700;letter-spacing:-.2px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.obn i{font-size:10px;font-weight:700;font-style:normal;letter-spacing:.6px;
  text-transform:uppercase;color:var(--aqua)}
.obright{display:flex;align-items:center;justify-content:flex-end;gap:14px 22px;
  flex-wrap:wrap;flex:1 1 240px}
.obnote{flex:1 1 175px;min-width:0;font-size:12.5px;line-height:1.45;
  color:var(--onp2);text-wrap:pretty}
.obp{font-family:var(--f-display);font-size:clamp(26px,4.4vw,34px);font-weight:700;
  line-height:1;letter-spacing:-1px;font-variant-numeric:tabular-nums;
  white-space:nowrap}
/* The shop's own mark, self-hosted (pipeline.shop_logos), never hotlinked --
   the same rule the phone photos follow. A shop whose site nominates no usable
   icon keeps its monogram, which is why this tile styles both.

   `--shop` is that mark's own colour, sampled from the file. A logo on a white
   plate on a white card looked stuck on rather than placed (owner 2026-08-04),
   so the tile is backed with a wash of the shop's colour and the card carries
   a bar of it down the left edge. It goes no further than that: the page still
   belongs to one brand, and fifteen saturated colours competing with the price
   would be a chart legend, not a price list. */
.ocard,.obest{--shop:var(--rgb-ink)}
/* WIDER than it is tall. Half these marks are wordmarks and half are round
   app icons; a square slot let the round ones fill it and squeezed the
   wordmarks into an unreadable strip. At 54x40 a wordmark uses the width, a
   round mark still renders round, and every shop name below starts at the
   same x. */
.olg{flex:none;display:flex;align-items:center;justify-content:center;
  overflow:hidden;width:54px;height:40px;border-radius:var(--r);font-size:12.5px;
  font-weight:700;letter-spacing:.3px;color:var(--mut);
  background:var(--tone);box-shadow:inset 0 0 0 1px var(--rule)}
/* a real mark needs no plate behind it: the box and its hairline read as a
   white border stuck around the logo (owner 2026-08-04). Only the monogram
   fallback keeps the tile, because a bare two-letter string needs a shape. */
.olg:has(img){background:none;box-shadow:none}
.olg img{width:100%;height:100%;object-fit:contain;border-radius:0}
/* The shop's colour lands on the HERO BAND only (owner 2026-08-04: "accent
   only for the hero"). Fifty-seven cards each washed in their own seller's
   colour turned the grid into a chart legend and made the one listing that
   matters no easier to find; one accented band does that job. The cards stay
   the site's own white. */
.ocard{background:var(--card);box-shadow:var(--sh-card)}
.ocard:hover{box-shadow:var(--sh-hover)}
.ocard.out{background:var(--bg);box-shadow:var(--sh-hair)}
/* the band takes the seller's colour as a wash off its leading edge and a
   hairline in the same hue -- dark enough to keep white text legible, present
   enough to read as that shop's row */
.obest{background:linear-gradient(100deg,rgba(var(--shop),.42),
    rgba(var(--shop),.10) 42%,rgba(var(--shop),0) 76%),var(--panel);
  box-shadow:inset 0 0 0 1px rgba(var(--shop),.55),var(--cta-dark-sh)}
.obest .olg{width:62px;height:46px;font-size:14px;color:var(--aqua);
  background:var(--onpfill);box-shadow:inset 0 0 0 1px var(--onp3)}
/* No plate here either. Which mark can appear at all is decided in the data,
   not by a backing colour: pipeline.shop_logos measures each file against this
   panel, and one that would vanish into it never reaches the band -- the
   monogram takes its place, in aqua, which always reads. */
.obest .olg:has(img){background:none;box-shadow:none}
.ocard.out .olg img{opacity:.65}

/* --- the listing cards -----------------------------------------------------
   Three lines, not three stacked blocks. The airy version of this card ran
   179px tall, and an iPhone with 57 listings turned the section into ten
   thousand pixels of scroll (owner 2026-08-04: "the page becomes long as
   fuck"). Same information, one grid: who and what it costs on line one, what
   the listing IS on line two, the colours it comes in on line three. */
.ocards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,320px),1fr));
  gap:9px}
/* align-content:start, or a card with no colour dots spreads its two rows to
   match the tallest card in the grid row and reads as half empty */
.ocard{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:7px 11px;
  align-items:center;align-content:start;padding:12px 14px;min-width:0;
  border-radius:var(--r);transition:box-shadow .14s,background .14s}
.ocard[hidden]{display:none}
/* NOT .on: that class is the rail's own active-button state (and the header
   nav's, and the pager's), so a `.on i` rule here was restyling the count
   inside every selected filter button */
.oname{grid-column:2;min-width:0;display:flex;flex-direction:column;gap:1px}
.oname b{font-size:14.5px;font-weight:700;letter-spacing:-.2px;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.oname i{font-size:9.5px;font-weight:700;font-style:normal;letter-spacing:.5px;
  text-transform:uppercase;color:var(--mut2)}
.oname i:empty{display:none}
.ocard.out .oname b{color:var(--mut)}
.ocard.best .oname i{color:var(--ac)}
.omoney{grid-column:3;display:flex;flex-direction:column;align-items:flex-end;
  gap:1px;text-align:right}
/* the price carries the amber -- a mark on a light surface, which is the only
   place the logo ever puts it */
.op{font-family:var(--f-display);font-size:18px;font-weight:700;line-height:1.1;
  letter-spacing:-.6px;color:var(--ink);font-variant-numeric:tabular-nums;
  white-space:nowrap}
.ocard.best .op{color:var(--ac)}
.ocard.out .op,.op.none{color:var(--mut)}
.odelta{font-size:10.5px;font-weight:600;color:var(--mut2);white-space:nowrap;
  font-variant-numeric:tabular-nums}
.odelta:empty{display:none}
/* line two spans the monogram gutter: the tags read as one strip rather than
   an indented paragraph */
.oc2{grid-column:1/-1;display:flex;align-items:center;gap:5px;flex-wrap:wrap}
/* what this listing IS: warranty first, then the configuration, then the
   details only some listings carry */
.otag{display:inline-flex;padding:3px 8px;border-radius:var(--r);font-size:11px;
  font-weight:600;line-height:1.3;color:var(--mut);background:var(--tone)}
.otag.chan{color:var(--tealD);background:var(--tealL)}
.otag.warn{color:var(--acd);background:var(--amsoft)}
.ocard.out .otag.chan{color:var(--mut);background:var(--tone)}
/* stock is a state, not a sentence: a dot and two words, same three colours
   every surface on this site uses for in / unreported / out. It closes the tag
   strip rather than floating in its own corner. */
.obadge{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:5px;
  padding:3px 8px;border-radius:var(--r);font-size:11px;font-weight:700;
  white-space:nowrap;color:var(--tealD);background:var(--tealL)}
.obadge i{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.obadge.unk{color:var(--acd);background:var(--amsoft)}
.obadge.no{color:var(--mut);background:var(--tone)}
/* colours are dots with a title, not dots with a caption: three paint names
   spelled out is a second paragraph on every card, and the swatch is the part
   a buyer actually recognises */
.osws{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:5px;align-items:center}
.osw{width:15px;height:15px;border-radius:50%;flex:none;
  background:var(--paint,repeating-linear-gradient(45deg,var(--rule) 0 3px,
    var(--bg) 3px 6px));box-shadow:inset 0 0 0 1px rgba(var(--rgb-ink),.18)}
.ocard.out .osw{opacity:.55}
/* the cap. Every listing is in the HTML -- crawlers and a reader with no
   JavaScript get all of them -- but a phone with 57 of them shows six and
   offers the rest, or the price section is the whole page. */
.omore{width:100%;min-height:42px;font:inherit;font-size:12.5px;font-weight:700;
  cursor:pointer;border-radius:var(--r);border:1px solid var(--rule);
  background:var(--card);color:var(--lnk)}
.omore:hover{border-color:var(--teal);background:var(--tint)}
.omore[hidden]{display:none}
.oempty{display:flex;flex-direction:column;gap:7px;align-items:center;
  text-align:center;padding:42px 20px;border:1px dashed var(--rule);
  border-radius:var(--r)}
.oempty b{font-family:var(--f-display);font-size:20px;font-weight:700;
  letter-spacing:-.4px;color:var(--ink)}
.oempty span{font-size:13.5px;color:var(--mut);max-width:320px;text-wrap:pretty}
@media(max-width:760px){
/* --- mobile ----------------------------------------------------------------
   Five labelled groups is most of a phone screen before a single price is on
   it, so only the two axes a buyer actually reaches for stay open and the rest
   fold behind one button. The labels are what must survive the fold:
   flattening the axes into a single unlabelled cloud was tried and "Not
   stated" beside "Import" beside "In stock" said nothing about which question
   each answers (owner 2026-08-02). */
.ofilt{gap:11px 18px;padding:12px 13px}
/* folded by a class on the panel, not a hidden attribute on each group: the
   attribute would survive a rotation into desktop width and leave the panel
   half empty */
.ofilt.folded .fgrp.more{display:none}
.fgrp{flex:1 1 100%}
.fb{min-height:36px;padding:6px 11px;font-size:12px}
.freset{margin-left:0;align-self:flex-start}
.fmore{display:flex;align-items:center;justify-content:center;width:100%;
  min-height:40px;font:inherit;font-size:12.5px;font-weight:700;cursor:pointer;
  border-radius:var(--r);border:1px solid var(--rule);background:var(--card);
  color:var(--tx)}
.fmore[aria-expanded="true"]{border-color:var(--teal);color:var(--lnk)}
.obest{padding:14px 15px}
.ocards{grid-template-columns:1fr}
.ocard{padding:11px 13px}}
/* the shop's import market, where a listing names one */
.rgtag{display:inline-block;margin-left:7px;padding:1px 7px;border-radius:var(--r);
  font-size:10.5px;font-weight:700;letter-spacing:.3px;
  background:var(--tealL);color:var(--tealD);vertical-align:1px}
.specsum{font-family:var(--f-display);font-size:15px;font-weight:600;
  letter-spacing:-.2px;color:var(--ink);margin:16px 0 8px}
/* verdict, split into scannable parts */
.vd{margin:24px 0 0;padding:20px 22px 8px;border-radius:var(--r);background:var(--card);
  box-shadow:var(--sh-card)}
.vdh{display:flex;align-items:center;gap:10px;margin-bottom:4px}
.vdh .ic{color:var(--ac)}
.vd h2{font-family:var(--f-display);font-size:19px;font-weight:700;
  letter-spacing:-.4px;margin:0}
.vd p{margin:10px 0 14px;font-size:15.5px;line-height:1.7;color:var(--ink2);max-width:76ch}
.gb{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:16px 0 4px}
.gb>div{border-radius:var(--r);padding:14px 16px}
.gb .gd{background:var(--tint);box-shadow:inset 0 0 0 1px var(--tint2)}
.gb .bd{background:var(--amsoft);box-shadow:inset 0 0 0 1px var(--amline)}
.gb h3{font-size:12px;text-transform:uppercase;letter-spacing:.7px;margin:0 0 9px;
  font-weight:700;color:var(--mut)}
.gb ul{margin:0;padding:0;list-style:none}
.gb li{display:flex;gap:9px;align-items:flex-start;font-size:14px;line-height:1.5;
  color:var(--ink2);padding:4px 0}
.gb .gd .ic{color:var(--teal);margin-top:2px}
.gb .bd .ic{color:var(--lnk);margin-top:2px}
.wf{display:flex;gap:10px;align-items:flex-start;padding:11px 0;
  border-top:1px solid var(--rule)}
.wf p{margin:0;font-size:14.5px;line-height:1.6}
.wf b{color:var(--ink);font-weight:700}
.wf .ic{margin-top:3px;color:var(--mut)}
/* spec rows: icon + label */
table.cmp td.ph .sl{display:inline-flex;align-items:center;gap:9px}
table.cmp td.ph .ic{color:var(--lnk);opacity:.75}
/* what reviewers said: the digest block. Reuses the caveat card's surface so
   it reads as part of the page rather than a bolted-on widget. */
.reviewers{margin:30px 0 0}
.reviewers .rv{margin:10px 0 0;font-size:16.5px;line-height:1.6;color:var(--ink);
  font-weight:600}
.reviewers .rbest{margin:6px 0 0;font-size:14px;color:var(--mut)}
.reviewers h3{margin:20px 0 8px;font-size:13px;text-transform:uppercase;
  letter-spacing:.6px;color:var(--lnk)}
.rpts{list-style:none;padding:0;margin:0;display:grid;gap:10px}
.rpts li{background:var(--card);border-radius:var(--r);padding:13px 16px;
  box-shadow:var(--sh-card);font-size:14.5px;line-height:1.55;color:var(--ink)}
.rpts li b{display:block;margin-bottom:2px}
.src{display:inline-block;margin-top:5px;font-size:12.5px;color:var(--mut);
  text-decoration:none}
a.src:hover{color:var(--lnk);text-decoration:underline}
.rev{background:var(--card);border-radius:var(--r);padding:14px 17px;
  box-shadow:var(--sh-card);margin-top:10px}
.rev h4{margin:0 0 6px;font-size:13.5px;font-weight:700}
.rev p{margin:0;font-size:14.5px;line-height:1.6;color:var(--ink)}

/* caveats as cards, not a bullet list */
.cavs{display:grid;gap:10px;margin-top:12px}
.cav{display:flex;gap:11px;align-items:flex-start;background:var(--card);border-radius:var(--r);
  padding:14px 17px;box-shadow:var(--sh-card)}
.cav>.ic{color:var(--lnk);margin-top:2px}
.cav .ct{display:block;font-size:12px;font-weight:700;text-transform:uppercase;
  letter-spacing:.6px;color:var(--lnk);margin-bottom:3px}
.cav p{margin:0;font-size:14.5px;line-height:1.55;color:var(--ink)}
.cav q{display:block;margin-top:7px;font-size:14px;font-style:italic;color:var(--mut);
  border-left:2px solid rgba(var(--rgb-ink),.12);padding-left:11px;quotes:none}
@media(max-width:640px){.gb{grid-template-columns:1fr}}

/* the .pgrid device card itself lives in pages._CSS: the homepage draws it too
   and loads that sheet alone. What follows is hub-only furniture. */

/* search: hidden until the script unhides it, so with JS off the page is a
   plain paginated directory rather than a dead input */
.psearch{display:none;position:relative;margin:20px 0 0}
.psearch input{width:100%;font:inherit;font-size:16px;font-weight:600;
  color:var(--ink);background:var(--card);border:0;border-radius:var(--r);
  padding:15px 17px 15px 46px;
  box-shadow:var(--sh-card);
  transition:box-shadow .16s}
.psearch input:focus{outline:none;
  box-shadow:inset 0 0 0 2px var(--teal)}
.psearch input::placeholder{color:var(--mut2);font-weight:500}
.psearch svg{position:absolute;left:17px;top:50%;transform:translateY(-50%);
  color:var(--mut);pointer-events:none}
.pmeta{margin:14px 0 0;font-size:13px;color:var(--mut)}
/* results render as the page's own .pgrid cards -- a full-width row with the
   name at one end and the price 1000px away at the other wasted the space
   (owner 2026-08-01: "search results are too wide") */
.pempty{padding:26px 4px;font-size:14.5px;color:var(--mut)}

/* brands: the .bsec grid lives in pages._CSS -- the homepage and /best hub
   render the same section and load that sheet alone. */

/* pagination */
.pgnav{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:6px;margin:30px 0 4px}
.pgnav a,.pgnav span{min-width:40px;height:40px;display:inline-flex;
  align-items:center;justify-content:center;padding:0 13px;border-radius:var(--r);
  font-size:14px;font-weight:700;color:var(--tx);background:var(--card);
  box-shadow:var(--sh-hair)}
.pgnav a:hover{text-decoration:none;color:var(--lnk);background:var(--card);
  box-shadow:inset 0 0 0 1px var(--teal)}
.pgnav .on{color:var(--onp);background:var(--teal);box-shadow:none}
.pgnav .gap{background:none;box-shadow:none;color:var(--mut);min-width:20px;
  padding:0}
@media(max-width:560px){.pgnav a,.pgnav span{min-width:36px;height:36px;
  padding:0 9px;font-size:13px}}

/* ---- /phone hub, 2026-08-14 ----
   The search was a bare input floating between a heading and 26 brand chips,
   which made the one tool on the page read as furniture. It gets a card of
   its own, and the page picks up the two entry points a buyer who does not
   know a model name actually needs: what matters to them, and what they can
   spend. */
.phero{background:var(--card);border-radius:var(--r);box-shadow:var(--sh-card);
  padding:clamp(20px,3.4vw,34px) clamp(18px,3vw,32px) clamp(18px,2.6vw,26px);
  margin:14px 0 0}
.phero h1{margin:0 0 8px}
.phero .lead{margin:0 0 18px}
.phero .psearch{margin:0}
.pstat{display:flex;flex-wrap:wrap;gap:7px 22px;margin:15px 0 0;font-size:13px;
  color:var(--mut)}
.pstat b{color:var(--ink);font-weight:700}
.mtr{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0 0}
.mtr a{display:inline-flex;align-items:center;gap:8px;font-size:13.5px;
  font-weight:700;color:var(--tx);background:var(--card);
  border:1px solid var(--rule);border-radius:var(--r-pill);padding:9px 16px;
  transition:color .16s,border-color .16s}
.mtr a:hover{text-decoration:none;color:var(--lnk);border-color:var(--teal)}
.mtr a svg{flex:none;color:var(--teal)}
.btiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));
  gap:12px;margin-top:14px}
.btile{display:flex;flex-direction:column;gap:2px;padding:18px 20px;
  border-radius:var(--r);background:var(--card);box-shadow:var(--sh-card);
  transition:transform .16s cubic-bezier(.4,0,.2,1),box-shadow .16s}
.btile:hover{text-decoration:none;transform:translateY(-3px);
  box-shadow:0 12px 26px rgba(var(--rgb-ink),.11),inset 0 0 0 1px var(--tint2)}
.btile .bl{font-size:9.5px;font-weight:700;letter-spacing:1px;
  text-transform:uppercase;color:var(--mut)}
.btile .bv{font-family:var(--f-display);font-size:26px;font-weight:700;
  letter-spacing:-1px;color:var(--ink);line-height:1.15}
.btile .bg{margin-top:9px;font-size:12.5px;font-weight:700;color:var(--lnk)}
.btile .bg i{font-style:normal;display:inline-block;transition:transform .16s}
.btile:hover .bg i{transform:translateX(3px)}
/* one card cut into three, not three cards: these are one claim in three
   parts, and three separate panels made them read as three offers */
.trust{display:grid;grid-template-columns:repeat(auto-fit,minmax(248px,1fr));
  margin:34px 0 0;background:var(--card);border-radius:var(--r);
  box-shadow:var(--sh-card);overflow:hidden}
.trust .tr{display:flex;gap:13px;padding:20px 22px}
.trust .tr+.tr{box-shadow:inset 1px 0 0 var(--rule)}
.trust .ti{flex:none;display:flex;align-items:center;justify-content:center;
  width:34px;height:34px;border-radius:var(--r-sm);background:var(--tealL);
  color:var(--tealD)}
.trust b{display:block;font-size:14.5px;font-weight:700;letter-spacing:-.2px;
  color:var(--ink)}
.trust p{margin:4px 0 0;font-size:13px;line-height:1.5;color:var(--mut)}
@media(max-width:760px){
.btiles{grid-template-columns:1fr 1fr}
.trust{grid-template-columns:1fr}
.trust .tr+.tr{box-shadow:inset 0 1px 0 var(--rule)}
}

/* paired sections */
.d2{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start;
  margin-top:34px}
/* the splits, sized to what each side actually holds: a filter rail plus
   seven listing cards is not the same amount of page as four stat tiles and
   a sparkline, and 50/50 left a column of white beside every short one */
.d2.pw{grid-template-columns:1.62fr 1fr}
.d2.pn,.d2.ps{grid-template-columns:1fr 1.28fr}
/* same widths as pn, right column sticks instead of stretching */
.d2.ps .dcol:last-child{position:sticky;top:calc(var(--hdr-h) + 12px);
  align-self:start}
.d2 .dcol>h2.grp:first-child,.d2 .dcol>.decide:first-child{margin-top:0}
.d2 .dcol>*:first-child{margin-top:0}
/* the split modifiers are .d2.pw / .d2.pn, so the stacking rule has to match
   their specificity or it silently loses and the pairs stay two-up on a phone */
@media(max-width:900px){.d2,.d2.pw,.d2.pn,.d2.ps{grid-template-columns:1fr;gap:0}
.d2.pw .dcol:last-child,.d2.ps .dcol:last-child{position:static}
.d2 .dcol+.dcol{margin-top:34px}}
/* in-page nav. The site header is sticky at top:0 but carries a 14px top
   margin, so its box actually rests at y=14 and ends at 70 -- park the rail at
   8px and it parks UNDERNEATH the header, which outranks it on z-index. Both
   anchor target has to clear it or it lands with its heading hidden behind
   the bar. One variable, so the two cannot drift apart. */
:root{--hdr-h:70px}
@media(max-width:620px){:root{--hdr-h:62px}}
/* NOT a second navbar (owner 2026-08-14, twice). Restyling it did not help,
   because the problem was never the styling: a STICKY horizontal row of
   links is chrome no matter what it is painted like, and it was glued
   permanently under the real one. It does not stick. It sits under the hero,
   reads as a sentence, and scrolls away like the content it is. */
.drail{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 6px;
  margin:16px 0 0;font-size:13px}
.drail .dk{font-weight:700;color:var(--mut);margin-right:2px}
.drail a{position:relative;font-weight:600;color:var(--lnk);padding:2px 0}
.drail a:not(:last-child)::after{content:"\00b7";position:absolute;right:-6px;
  color:var(--faint);font-weight:400}
.drail a:hover{text-decoration:underline}
#verdict,#prices,#ratings,#specs,#faq,#similar{
  scroll-margin-top:calc(var(--hdr-h) + var(--rail-h) + 14px)}
@media(max-width:560px){.drail{padding:5px}
.drail a{font-size:12px;padding:7px 12px}}
/* ---- the offers list, 2026-08-14 ----
   It was a responsive grid of cards, so inside a half-width column it went
   two-across and every shop name over ~14 characters was ellipsed: "Gazi
   Electr...", "Apple Gadg...". A price list is a LIST. One row per listing,
   hairline separated, the name in a column that can actually hold it.
   The DOM contract is untouched -- same article.ocard, same data-* attributes,
   same .odelta node -- because the filter script reads all of it. */
.ocards{grid-template-columns:1fr;gap:0;
  border-top:1px solid var(--rule)}
.ocard{grid-template-columns:auto minmax(0,1fr) auto auto;gap:1px 13px;
  padding:11px 6px;border-radius:0;background:none;box-shadow:none;
  border-bottom:1px solid var(--rule)}
.ocard:hover{background:var(--tone);box-shadow:none}
.ocard.out{background:none;box-shadow:none;opacity:.72}
.ocard.out:hover{background:var(--tone)}
/* the name gets to be a name: ellipsis was hiding which shop this is, which
   is the entire point of the row */
/* The cheapest listing is already drawn in full as the band above, so the
   list does not repeat it. CSS rather than dropping it server-side: the
   filter script re-decides which listing is cheapest on every click and moves
   the .best class itself, so hiding whatever currently carries it keeps the
   band and the list in step, and the counts still include it because it IS
   one of the listings. */
.ocard.best{display:none}
/* row 1 only: spanning 1/3 put the name in the same cell as the tag strip,
   which is why "CHEAPEST IN STOCK" printed on top of "Import 128GB US" */
.oname{grid-row:1}
.oname b{white-space:normal;overflow:visible;text-overflow:clip;
  line-height:1.3}
.omoney{grid-column:4;grid-row:1/3;align-self:center}
/* the colour dots take a column of their own rather than a full-width third
   ROW -- that row was most of the 121px these listings were running to */
.osws{grid-column:3;grid-row:1/3;align-self:center;max-width:76px;
  justify-content:flex-end}
/* tags as quiet text on one line, not a second row of pills under every
   listing -- with six listings that was six chip rows. .oc2 .otag, not
   .otag: the pill styling is set at that depth and a one-class reset loses. */
.oc2{grid-column:2;grid-row:2;gap:0 4px;margin-top:1px}
/* the second selector exists to out-specify `.ocard.out .otag.chan`, four
   classes deep, which was still painting a pill behind the channel word on
   every sold-out listing */
.oc2 .otag,.ocard.out .oc2 .otag.chan{font-size:11.5px;font-weight:600;
  color:var(--mut);background:none;box-shadow:none;border:0;padding:0;
  border-radius:0}
/* The backslash is DOUBLED on purpose. This sheet is a plain triple-quoted
   string, not a raw one, so a single backslash followed by digits is an octal
   escape: Python was emitting a NUL byte into the CSS and the browser dropped
   the rules around it. Any CSS escape written here needs the same doubling. */
.oc2 .otag+.otag::before{content:"\00b7";margin-right:5px;color:var(--faint)}
.oc2 .otag.chan{color:var(--tealD);font-weight:700}
.oc2 .otag.warn{color:var(--acd);font-weight:700}
.ocard .olg{width:44px;height:34px}
@media(max-width:560px){
.ocard{padding:12px 2px;gap:2px 10px}
.ocard .olg{width:38px;height:30px}
.oname b{font-size:13.5px}
.op{font-size:16px}
}

/* "compare at this price": the neighbours, each with what it does better */
.vscards{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));
  gap:12px;margin-top:14px}
.vscard{display:flex;align-items:center;gap:13px;padding:14px 15px;
  border-radius:var(--r);background:var(--card);box-shadow:var(--sh-card);
  transition:transform .16s cubic-bezier(.4,0,.2,1),box-shadow .16s}
.vscard:hover{text-decoration:none;transform:translateY(-3px);
  box-shadow:0 12px 26px rgba(var(--rgb-ink),.11),inset 0 0 0 1px var(--tint2)}
.vsh{display:flex;align-items:center;justify-content:center;width:52px;
  height:70px;flex:none;border-radius:var(--r-sm);background:var(--plate);
  box-shadow:var(--sh-hair)}
.vsh img{max-width:100%;max-height:100%;width:auto;height:auto;
  object-fit:contain;display:block}
.vsh.noimg{color:var(--faint)}
.vsb{min-width:0;flex:1}
.vsk{display:block;font-size:9.5px;font-weight:700;letter-spacing:.9px;
  text-transform:uppercase;color:var(--mut)}
.vsn{display:block;margin-top:2px;font-size:14.5px;font-weight:700;
  letter-spacing:-.3px;color:var(--ink);line-height:1.25}
.vsp{display:block;margin-top:2px;font-size:14px;font-weight:700;
  letter-spacing:-.3px;color:var(--ink)}
.vstag{display:block;margin-top:6px;font-size:12px;font-weight:600;
  color:var(--mut)}
.vsgo{flex:none;font-size:22px;line-height:1;color:var(--faint)}
.vscard:hover .vsgo{color:var(--teal)}
/* the good/bad headings carry their own colour -- both were grey, so the two
   boxes only differed by a wash most people do not consciously register */
.gb .gd h3{color:var(--teal)}
.gb .bd h3{color:var(--ac)}
/* A bare stroke tick and a bare stroke cross are the two most generic marks
   on the internet (owner 2026-08-14). Filled discs: the mark is knocked out
   of the colour instead of drawn in it, which is the same move the logo's
   own checkmark makes. */
/* A tinted disc with the glyph drawn IN the colour, not knocked out of it:
   white-on-solid clipped the tick against the disc's own radius and left the
   good column looking like a row of plain dots. */
.gb li,.wf{align-items:flex-start}
.gb li .ic,.wf .ic{flex:none;box-sizing:content-box;width:15px;height:15px;
  padding:4px;border-radius:50%;margin-top:1px;overflow:visible}
.gb .gd .ic,.wf.yes .ic{background:var(--tealL);color:var(--tealD)}
.gb .bd .ic,.wf.no .ic{background:var(--amsoft);color:var(--acd)}
/* shape, not concealment: a lead paragraph that steps down into the rest,
   inside a tinted panel on a teal rule. Same words that read as a slab when
   they were four flat paragraphs of identical weight. */
.vdwhy{margin:20px 0 6px;padding:18px 22px;border-radius:var(--r);
  background:var(--tint);border-left:3px solid var(--teal)}
.vdwhy h3{margin:0 0 9px;font-size:10.5px;font-weight:700;letter-spacing:.9px;
  text-transform:uppercase;color:var(--teal)}
/* fills the panel: the 64ch cap was leaving a third of the box empty on the
   right, which read as the text having stopped short rather than as a measure */
.vd .vdwhy p{max-width:none;margin:0 0 11px}
.vd .vdwhy p:last-child{margin-bottom:0}
.vd .vdwhy p:first-of-type{font-size:16.5px;line-height:1.6;color:var(--ink)}
.vd .vdwhy p:first-of-type+p{padding-top:11px;
  border-top:1px solid rgba(var(--rgb-teal),.16)}
.vd .vdwhy p~p{font-size:14.5px;line-height:1.65;color:var(--mut)}
/* equal-height slabs: the paired cards ended wherever their content did, so
   every row of the page finished ragged */
/* Stretch only where both sides hold comparable amounts. Ratings against spec
   tiles, caveats against the FAQ -- those two want to finish level. Prices
   against price history do NOT: a filter rail plus seven listing cards is
   twice the height of a sparkline, and stretching the short one produced a
   price-history card three screens tall with nothing in the bottom two
   (owner 2026-08-14: "why price history so long"). It takes its natural
   height and rides down with the list instead. */
.d2.pn{align-items:stretch}
.d2.pn .dcol{display:flex;flex-direction:column}
.d2.pn .dcol>.decide,.d2.pn .dcol>#faq,.d2.pn .dcol>.faq{flex:1}
.d2.pw .dcol:last-child{position:sticky;top:calc(var(--hdr-h) + 12px);
  align-self:start}
.kst{align-items:flex-start}
.ksgrid{grid-auto-rows:1fr}
/* the FAQ ships its own eyebrow + big centred question heading, sized to be
   the widest thing on a page. Full width now, so it finally has the room it
   was built for -- and the fallback keeps it civil if it ever pairs again. */
.d2 .dcol .faq h2{font-size:19px;text-align:left}
.d2 .dcol .faq .eyebrow,.d2 .dcol .faq .fq{text-align:left}
/* four stat chips across the top of the history card, not three-then-one */
.d2.pw .ph .phstat{display:grid;grid-template-columns:repeat(2,1fr);gap:7px}
.d2.pw .ph .phstat span{padding:8px 10px}
.wfs{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:16px 0 12px}
.wf{border-top:0;padding:14px 16px;border-radius:var(--r);align-items:flex-start}
.wf.yes{background:var(--tint);box-shadow:inset 0 0 0 1px var(--tint2)}
.wf.no{background:var(--amsoft);box-shadow:inset 0 0 0 1px var(--amline)}
.wf.yes .ic{color:var(--teal)}
.wf.no .ic{color:var(--ac)}
.wf b{display:block;margin-bottom:2px}

@media(max-width:640px){
.wfs{grid-template-columns:1fr;gap:9px}
.gb{grid-template-columns:1fr;gap:10px}
.vscard{padding:12px 13px;gap:11px}
.d2 .dcol+.dcol{margin-top:28px}
}

/* Mobile reads copy -> donate card -> the rest, so the one action on the page
   is not 750px down the scroll; desktop puts the card in its own column beside
   the whole block. */
.shero{margin:2px 0 0}
.shero>*+*{margin-top:22px}
@media(min-width:960px){
.shero{display:grid;grid-template-columns:1.08fr .92fr;gap:0 40px;
align-items:start}
.shero>*+*{margin-top:0}
.sh-copy{grid-column:1;grid-row:1}
.sh-more{grid-column:1;grid-row:2}
.bkcard{grid-column:2;grid-row:1/span 2}
}
.shero h1{margin:18px 0 0}
.slead{font-size:16.5px;line-height:1.68;color:var(--tx);max-width:60ch;margin:14px 0 0}
.slead b{color:var(--ink);font-weight:600}
.tchips{display:flex;flex-wrap:wrap;gap:8px;margin:20px 0 0}
.tchip{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;
font-weight:600;color:var(--tx);background:var(--card);
border:1px solid var(--rule);border-radius:var(--r);padding:7px 13px}
.tchip svg{flex:none;color:var(--tealD)}

/* bKash pink is a payment brand's own mark, so it stays -- but it is theirs,
   not ours: it is confined to this one card and spelled from a token. */
.bkcard{position:relative;overflow:hidden;background:var(--card);border-radius:var(--r);
padding:20px clamp(18px,3vw,24px) 22px;
box-shadow:var(--sh-lift),inset 0 0 0 1px rgba(var(--rgb-bkash),.16)}
.bkhead{position:relative;display:flex;align-items:center;justify-content:space-between;
gap:12px;flex-wrap:wrap}
.bkhead img{display:block}
.bktag{font-size:11px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;
color:var(--c-bkash-700);background:rgba(var(--rgb-bkash),.10);padding:6px 11px;border-radius:var(--r)}
.bkl{margin:18px 0 0;font-size:13px;font-weight:600;color:var(--mut)}
.bknum{display:flex;align-items:center;gap:12px;margin:7px 0 0;padding:12px 12px 12px 16px;
border-radius:var(--r);background:var(--card);
border:1.5px dashed rgba(var(--rgb-bkash),.36);flex-wrap:wrap}
.bknum b{flex:1;min-width:150px;font-size:clamp(21px,5.2vw,26px);font-weight:700;
letter-spacing:1px;line-height:1.25;font-variant-numeric:tabular-nums;
-webkit-user-select:all;user-select:all}
.copyb{flex:none;display:inline-flex;align-items:center;gap:7px;min-height:44px;
padding:0 16px;border:0;border-radius:var(--r);font:inherit;font-size:13.5px;
font-weight:700;color:var(--onp);cursor:pointer;background:var(--c-bkash);
box-shadow:0 4px 12px rgba(var(--rgb-bkash),.32);transition:filter .18s,background .18s}
.copyb:hover{filter:brightness(1.07)}
.copyb:focus-visible{outline:3px solid rgba(var(--rgb-bkash),.45);outline-offset:2px}
.copyb.done{background:var(--teal);box-shadow:none}
.amts{display:flex;flex-wrap:wrap;gap:8px;margin:14px 0 0}
.amt{font-size:13px;font-weight:700;color:var(--c-bkash-700);background:rgba(var(--rgb-bkash),.08);
border-radius:var(--r);padding:8px 13px;font-variant-numeric:tabular-nums}
.amt.any{color:var(--mut);background:rgba(var(--rgb-ink),.05)}
.bksteps{counter-reset:s;list-style:none;margin:18px 0 0;padding:0}
.bksteps li{counter-increment:s;position:relative;margin:9px 0;padding-left:30px;
font-size:14px;line-height:1.5;color:var(--tx)}
.bksteps li::before{content:counter(s);position:absolute;left:0;top:1px;
display:flex;align-items:center;justify-content:center;width:21px;height:21px;
border-radius:var(--r);background:rgba(var(--rgb-bkash),.1);color:var(--c-bkash-700);font-size:11.5px;
font-weight:700}
.bkfine{margin:16px 0 0;font-size:12.5px;line-height:1.55;color:var(--mut)}

.cgrid{display:grid;grid-template-columns:1fr;gap:14px;margin:14px 0 0}
@media(min-width:760px){.cgrid{grid-template-columns:repeat(3,1fr)}}
.ccard{background:var(--card);border:1px solid var(--rule);
border-radius:var(--r);padding:19px}
.ccard .ci{display:flex;align-items:center;justify-content:center;width:38px;
height:38px;margin:0 0 12px;border-radius:var(--r);background:var(--tealL);color:var(--teal)}
.ccard h3{margin:0;font-family:var(--f-display);font-size:16px;font-weight:700;
letter-spacing:-.2px}
.ccard p{margin:6px 0 0;font-size:14px;line-height:1.55;color:var(--mut)}
.ccard a{font-weight:600}
