/* ══════════════════════════════════════════════════════════════════════
   coa-straight.css — a certificate, a lab report or a lot sheet is drawn
   FLAT. Every page except standards.html.

   OWNER, 2026-08-26: "also the coas are crooked but the client wants them
   to be straight."

   ── WHY THIS SHEET IS SCOPED TO AN ATTRIBUTE ────────────────────────────
   lib.linkCss() links a lane sheet on EVERY built page, standards.html
   included, and standards.html belongs to a different lane in this same
   pass. So the sheet does nothing until 30-coa-straight.mjs stamps
   `data-cc-coa-straight` on <html>, and the module stamps every page BUT
   standards.html. The boundary between the two lanes is therefore a fact on
   disk that either side can check, not a promise in a comment.

   THE REASON standards.html IS EXCLUDED IS THE OWNER'S BRIEF, NOT A
   COLLISION HAZARD. That page is assigned to the research-layout lane,
   which already writes `body .pg-plate-inner img.pg-coa { transform: none }`
   at final/css/research-layout.css:143 — an earlier draft of this comment
   cited :121, which is prose inside that file's opening block. The only
   property this sheet and that one both declare is `transform`, and both
   want the same value, so arming standards.html would have been REDUNDANT
   rather than destructive: a sheet that declares only `transform` and
   `rotate` cannot reach that lane's isolation, z-index or width work.
   Redundant is still not ours to write.

   ── WHY !important, WHICH IS NOT USUALLY THE ANSWER HERE ────────────────
   A document tilt on this engine can arrive by four routes, and NONE of
   them loses to an ordinary later rule:

     1. a page-level <style> block. A lane sheet links at the very END of
        <head> — which beats a HEAD-level <style> on document order, and
        that is where `.pg-coa`'s -3.5deg sits (standards.html:178). But it
        LOSES on document order to the body-level <style> block every
        content page carries after </head>: standards.html closes <head> at
        1798 and opens a <style> at 1803; coa-library 1578/1582; contact
        1583/1587; product 283/287; index 361/365. An earlier draft of this
        comment said an ordinary rule already wins route 1. It does not, for
        any body-level block — which is exactly why route 1 needs the
        !important too. Do not drop it on the old reasoning.
     2. an inline style="transform:rotate(...)" attribute.
     3. a page-level <style> rule that is itself !important.
     4. a @keyframes entrance that starts rotated — `sfx-kf-tilt` in
        theme.css opens at `rotate(-2deg) translateY(14px)`.

   An IMPORTANT AUTHOR declaration outranks 1, 2 and 4 outright: it beats a
   normal inline attribute (2) regardless of specificity, and — the one
   worth knowing — important author declarations sit ABOVE the animation
   origin in the cascade, so (4) is covered by the same line and needs no
   keyframe of its own. Both were verified against the live site, not
   assumed: an inline `style="transform:rotate(-3.5deg)"` and a
   `both`-filled keyframe holding -8deg both went to 0 with only these rules
   injected.

   ROUTE 3 IS THE HONEST LIMIT, AND IT IS STATED RATHER THAN PAPERED OVER.
   Two !important declarations are decided on specificity, and the rules
   below run 0,2,2 at their widest. A page-level !important rule carrying an
   ID selector — 1,x,x — would still win, and the probe proved it: a
   synthetic `#host .cert { transform: rotate(6deg) !important }` survived
   this sheet untouched.

   THE ENUMERATION OF THOSE ID RULES IS NO LONGER FROZEN IN THIS COMMENT.
   An earlier draft claimed the site's ONLY ID-scoped !important transform
   was `#product-grid article:hover img { transform: scale(1.44) !important }`.
   It is not — products.html also carries
   `#product-grid article[data-category="accessories"]:hover img` at
   scale(2.2) !important, and admin.html carries an `#admin-sidebar` block
   that mixes a transform with !important declarations. The CONCLUSION held
   (none of them reaches a document image), but a search result reported as
   an exhaustive fact is how a wrong claim gets re-used. So
   30-coa-straight.mjs re-derives that list every pass — over the pages AND
   the stylesheets they link — and prints it. Read the pass log, not this
   paragraph.

   ── WHAT THIS SHEET MUST NOT TAKE ───────────────────────────────────────
   Rotation is FUNCTION on some elements and this sheet never names them.
   Measured on the live site, every one of these is legitimate and stays:

     svg.faq-caret            180deg   open/closed state of a <details>
     span.std-chev             90deg   open/closed state of a standards row
     span.pg-q-sign           135deg   plus turning into a minus
     .od-chev                 180deg   my-account order row toggle
     .coa-terms[open] .chev   180deg   the PDP's terms disclosure caret
     @keyframes cc-spin       360deg   the app-surfaces busy spinner
     .sf-new-tag               -4deg   the "NEW" ribbon — ornament, not a doc
     .cc-hv-a / .cc-hv-b   -7 / 8deg   hero VIAL photos — ornament, not a doc
     .pg-faq-img img        -2 / 2deg  vial photos on standards — not a doc
     .float-logo    --rot0/--rot1      drifting logo marks in a header band

   A blanket `img { transform: none }` would have taken the spinner, the
   ribbon and both hover lifts with it. Every selector below names a
   document.

   ── THE HOVER LIFTS ARE PRESERVED, NOT COLLATERAL ───────────────────────
   `transform:none !important` on a document would also erase the two lift
   idioms this client's body classes turn on, so both are re-declared
   WITHOUT their tilt further down:
     body.opt-img-hover-lift        .sf-thumb:hover img  -> translateY(-4px)
     body.opt-hover-feedback-playful .sf-hover-lift:hover -> translateY(-6px)
                                                             rotate(-0.4deg)
   The second one is the interesting case: it is a hover state that puts a
   real (if small) tilt on anything carrying .sf-hover-lift. On a product
   tile that is charm and it stays; on a certificate it is the owner's
   complaint arriving on hover, so the lift is kept and the -0.4deg is not.

   ── A STRAIGHTENED DOCUMENT CANNOT PUSH ANYTHING ────────────────────────
   `transform` is paint-only: it never contributes to layout, so no box on
   the page moves when a tilt comes off. The PAINTED ink of a rotated
   rectangle is strictly LARGER than its layout box (w*|cos|+h*|sin| wide —
   for the -3.5deg 219x309 certificate measured on standards.html, 18.9px
   wider and 13.4px taller than flat), so removing a rotation can only
   shrink what is drawn back inside a box that never changed. Overlap and
   horizontal overflow can improve; they cannot get worse.

   Measured anyway, because "it cannot" is how a defect gets shipped: this
   sheet was injected into all 20 customer-visible pages of the LIVE site at
   390x844 and 1440x900 and the counts read before and after — rotated
   elements, horizontally overflowing elements, pairwise image overlaps and
   document scrollWidth — are IDENTICAL on 39 of the 40 measurements. The
   fortieth is my-account.html at 390px, and it is not this sheet: a
   signed-out visitor is redirected to gate.html, which happened between the
   two reads. Confirmed by watching the navigation directly.
   ══════════════════════════════════════════════════════════════════════ */

/* ── 1. THE DOCUMENT ITSELF ──────────────────────────────────────────────
   Two class hooks the engine already owns, plus a signature on the artwork
   so a renamed class cannot smuggle a crooked certificate back in. The
   client's certificates are named `<sku>-coa.png`; a document is also
   recognised by what its alt text says it is, which is the one description
   that survives every refactor.

   THE `coa` ALT SIGNATURE IS ANCHORED, and that is not fussiness. An
   earlier draft carried `img[alt*="coa" i]` — an unanchored three-letter
   substring carrying `transform: none !important`, which matches "coarse",
   "coating", "cocoa" and "coalesce". In THIS vertical "coarse lyophilised
   powder" and "a fine coating on the vial wall" are ordinary alt copy, and
   matching one of them would have silently killed that photo's hover zoom.
   `[alt^="COA " i]` carries the trailing space for the same reason:
   `^="coa"` on its own still matches "Coating...". Every real COA this
   client ships is caught by the src signatures regardless
   (brand_assets/coas/ccl-*-coa.png), and "certificate of analysis" is
   already caught by the `certificate` line. */
html[data-cc-coa-straight] img.pg-coa,
html[data-cc-coa-straight] .sf-coa-thumb,
html[data-cc-coa-straight] .sf-coa-thumb img,
html[data-cc-coa-straight] img[src*="-coa." i],
html[data-cc-coa-straight] img[src*="-coa-" i],
html[data-cc-coa-straight] img[src*="/coa" i],
html[data-cc-coa-straight] img[alt*="certificate" i],
html[data-cc-coa-straight] img[alt*="lab report" i],
html[data-cc-coa-straight] img[alt*="test report" i],
html[data-cc-coa-straight] img[alt*="lot report" i],
html[data-cc-coa-straight] img[alt="COA" i],
html[data-cc-coa-straight] img[alt^="COA " i],
html[data-cc-coa-straight] img[alt^="COA:" i],
html[data-cc-coa-straight] img[alt*=" COA " i],
html[data-cc-coa-straight] img[alt$=" COA" i] {
  transform: none !important;
  /* The standalone rotate property is a SEPARATE cascade slot from the
     transform shorthand — a `rotate: -3deg` declaration is untouched by
     `transform: none`, so it is zeroed on its own line. */
  rotate: 0deg !important;
}

/* ── 2. THE FRAME AROUND IT ──────────────────────────────────────────────
   The tilt is as often on the plate as on the picture, so the plate is
   named. `.pg-plate` / `.pg-plate-inner` / `.pg-coa-frame` are the master's
   research-page certificate plate (today they exist only on standards.html,
   where the attribute above keeps this sheet out; they are named here so
   the idiom cannot regrow on a page this lane owns).

   `.coa2-fig` / `.coa2-paper` are the OTHER certificate frame this factory
   emits, and it is the one that actually lands on a page this lane owns.
   factory/build.mjs:4019 and engine/template/index.html:777 write the home
   page's proof band as

       <div class="fade-up coa2-figwrap">
         <figure class="coa2-fig">
           <div class="coa2-paper"><img alt="Certificate of analysis for ...">

   — so the certificate image is a GRANDCHILD of the <figure> and the
   innermost frame is a <div>. An earlier draft covered wrappers only
   through §2b's `:has(> img)` rules, which refuse `div` and used the CHILD
   combinator, so NOT ONE of them matched this shape: a tilt on `.coa2-fig`
   or `.coa2-paper` would have rotated the whole paper stack with nothing in
   this sheet touching it. Naming the two frames here keeps §2b's
   anti-`div` rule intact — there is still no blanket `div:has()`. */
html[data-cc-coa-straight] .pg-plate,
html[data-cc-coa-straight] .pg-plate-inner,
html[data-cc-coa-straight] .pg-coa-frame,
html[data-cc-coa-straight] .coa2-fig,
html[data-cc-coa-straight] .coa2-paper {
  transform: none !important;
  rotate: 0deg !important;
}

/* ── 2a. THE OUTERMOST WRAPPER, WITHOUT EATING ITS SCROLL REVEAL ─────────
   `.coa2-figwrap` is the third element in that stack and it is ALSO the
   `.fade-up` host: index.html:293 plays
   `.fade-up { transform: translateY(28px) }` ->
   `.fade-up.visible { transform: translateY(0) }`.
   A flat `transform: none !important` here would straighten a tilt that
   does not exist today and delete a scroll reveal that does, so it is
   scoped to the RESTING state instead — which is the only state a tilt
   would be visible in, because the pre-reveal state is `opacity: 0`.
   `.visible`'s own resting value is `translateY(0)`, i.e. identical to
   `none`, so this costs the reveal exactly nothing. `:not(.fade-up)` covers
   a build that emits the wrapper without the reveal class at all.
   A tilt applied to `.coa2-figwrap` through the transform shorthand DURING
   the reveal is out of this sheet's reach on purpose — and it is in reach
   of the module's scan, which reads `coa2-` as a document selector and
   reports it. */
html[data-cc-coa-straight] .coa2-figwrap.visible,
html[data-cc-coa-straight] .coa2-figwrap:not(.fade-up) {
  transform: none !important;
  rotate: 0deg !important;
}

/* ── 2b. THE FRAME, FOUND BY WHAT IT WRAPS ───────────────────────────────
   :has() IS ALONE IN ITS OWN BLOCK ON PURPOSE. A selector list is dropped
   WHOLE by any parser that does not understand one member of it, so a
   :has() sitting in the list above would take the entire document rule with
   it on an older engine. Here it can only lose itself.

   DESCENDANT, NOT CHILD. The child combinator was the bug in the first
   draft: every certificate this factory actually emits sits inside a frame
   div, so `figure:has(> img)` matched nothing. Deliberately still narrow in
   the other direction — <figure>/<picture>/<a> only, never `div`, because a
   div with a certificate somewhere inside it is as likely to be a grid cell
   with a hover transform of its own. */
html[data-cc-coa-straight] figure:has(img[src*="-coa." i]),
html[data-cc-coa-straight] figure:has(img[alt*="certificate" i]),
html[data-cc-coa-straight] figure:has(img.pg-coa),
html[data-cc-coa-straight] picture:has(img[src*="-coa." i]),
html[data-cc-coa-straight] a.sf-coa-link:has(img),
html[data-cc-coa-straight] a:has(img[src*="-coa." i]) {
  transform: none !important;
  rotate: 0deg !important;
}

/* ── 3. THE LIFTS, KEPT — WITHOUT THE TILT ───────────────────────────────
   Rule 1 is !important, so it would otherwise beat the hover states in
   theme.css and a certificate thumbnail would be the one image on the site
   that does not answer a pointer. Both idioms are restored here at their
   own translate, and only the rotate is left behind. The `a:has()` pair
   restores the lift on a card link that §2b has just flattened. */
html[data-cc-coa-straight] .sf-thumb:hover .sf-coa-thumb img,
html[data-cc-coa-straight] .sf-thumb:hover img.pg-coa,
html[data-cc-coa-straight] .sf-thumb:hover img[src*="-coa." i],
html[data-cc-coa-straight] .sf-thumb:hover img[alt*="certificate" i] {
  transform: translateY(-4px) !important;
}
html[data-cc-coa-straight] .sf-coa-thumb.sf-hover-lift:hover,
html[data-cc-coa-straight] .sf-coa-thumb.sf-hover-lift:focus-visible,
html[data-cc-coa-straight] .pg-coa-frame.sf-hover-lift:hover,
html[data-cc-coa-straight] .pg-coa-frame.sf-hover-lift:focus-visible,
html[data-cc-coa-straight] a.sf-hover-lift:has(img[src*="-coa." i]):hover,
html[data-cc-coa-straight] a.sf-hover-lift:has(img[src*="-coa." i]):focus-visible {
  /* theme.css plays this as translateY(-6px) rotate(-0.4deg). Same lift,
     no tilt. */
  transform: translateY(-6px) !important;
}

/* ── 3b. TOUCH: A LIFT THIS SHEET RESTORES MUST NOT STICK ON A PHONE ─────
   Rule 3 is both !important and more specific than anything the template
   writes, so on a touch device it would leave a certificate thumbnail stuck
   in its hover pose after a tap. This block takes it back off.

   THE TEMPLATE DOES NOT COVER THIS FOR US, AND AN EARLIER DRAFT OF THIS
   COMMENT SAID IT DID. It claimed "every built page carries, in its own
   <style>, @media (hover:none) { .sf-hover-lift:hover, .sf-hover-scale:hover,
   .sf-pressable:hover { transform: none !important } }". Measured across
   factory/output/core-chain-labs/*.html: TWO of 26 pages carry that rule —
   index.html:208 and products.html:129. product.html has a (hover:none)
   block, but it is `.sf-related a:hover .rel-card`, not the first-tap rule,
   and the remaining 23 pages carry no (hover:none) block at all.

   So on index.html and products.html this block RE-STATES a reset the
   template already performs; on every other page it ADDS one that never
   existed. Both are wanted, and the second is the deliberate choice — a
   certificate stuck in a hover pose is the owner's complaint arriving by
   another route. Do NOT read the template as guaranteeing a first-tap reset
   sitewide: it does not, and a hover transform written elsewhere on that
   assumption will stick. */
@media (hover: none) {
  html[data-cc-coa-straight] .sf-coa-thumb.sf-hover-lift:hover,
  html[data-cc-coa-straight] .sf-coa-thumb.sf-hover-lift:focus-visible,
  html[data-cc-coa-straight] .pg-coa-frame.sf-hover-lift:hover,
  html[data-cc-coa-straight] .pg-coa-frame.sf-hover-lift:focus-visible,
  html[data-cc-coa-straight] a.sf-hover-lift:has(img[src*="-coa." i]):hover,
  html[data-cc-coa-straight] a.sf-hover-lift:has(img[src*="-coa." i]):focus-visible {
    transform: none !important;
  }
}

/* ── 4. A VISITOR WHO ASKED FOR NO MOTION ────────────────────────────────
   theme.css drops the lift entirely under reduced motion; matched here so
   this sheet does not put back a movement the OS asked to remove. */
@media (prefers-reduced-motion: reduce) {
  html[data-cc-coa-straight] .sf-thumb:hover .sf-coa-thumb img,
  html[data-cc-coa-straight] .sf-thumb:hover img.pg-coa,
  html[data-cc-coa-straight] .sf-thumb:hover img[src*="-coa." i],
  html[data-cc-coa-straight] .sf-thumb:hover img[alt*="certificate" i],
  html[data-cc-coa-straight] .sf-coa-thumb.sf-hover-lift:hover,
  html[data-cc-coa-straight] .sf-coa-thumb.sf-hover-lift:focus-visible,
  html[data-cc-coa-straight] .pg-coa-frame.sf-hover-lift:hover,
  html[data-cc-coa-straight] .pg-coa-frame.sf-hover-lift:focus-visible,
  html[data-cc-coa-straight] a.sf-hover-lift:has(img[src*="-coa." i]):hover,
  html[data-cc-coa-straight] a.sf-hover-lift:has(img[src*="-coa." i]):focus-visible {
    transform: none !important;
  }
}
