/* ============================================================
   NJ INDOOR OUTDOOR — v2 SERVICE HERO, FOR THE PRICING PAGE

   THE SAME COMPONENT AS /junk-removal-page/'s AND /dumpster/'s
   hero: same class names, same structure, same measurements. Three
   pages now open the same way, which is the whole point — the hero is
   the one place a visitor compares them.

   IF YOU CHANGE THE HERO, CHANGE IT IN ALL THREE:
   v2/junk-removal.css, v2/dumpster.css and this file.

   junk-removal.css carries the FULL rationale — where the structure
   came from (coconutcleaningco.com's home hero, the reference this
   whole v2 build was built against), why the band is 78svh, why the
   headline is weight 400 when every other heading is 300, and why the
   scrim is one soft ellipse rather than a flat veil. Read it there
   before changing a number here. Only what is specific to THIS page
   is written out below.

   WHY THIS FILE EXISTS AT ALL, rather than the rules living in
   v2.css. /pricing/ is a 1:1 mirror of the live Webflow page and
   is styled by Webflow's own stylesheet. It cannot load v2.css: the
   two collide on *, html, body, a and img and the mirrored look goes
   with it. v2-nav.css already carries the header slice for exactly
   this reason; this is the hero slice, and it loads after it.

   THE BRIDGE BLOCK BELOW IS THE SAME TRICK v2-nav.css USES, and the
   same warning applies. Webflow owns `body`, `h1`, `p`, `a` and `img`
   on this page — its body is Arial 14/20 and its links underline — so
   without the bridge the hero renders in Arial with an underlined
   phone button. Every bridge rule is scoped to the .shero subtree and
   wrapped in :where(), which contributes no specificity, so each rule
   lands at the same weight the bare element selector carries in
   v2.css and the cascade below behaves as it does there. UNSCOPED IT
   WOULD RESTYLE THE WHOLE MIRRORED PAGE.

   VERIFIED BEFORE WRITING: none of `.btn`, `.shero`, `.phero` or
   `--accent-green` appears anywhere in Webflow's 137KB stylesheet, so
   nothing here can be captured by, or capture, a Webflow rule.
   ============================================================ */

/* Only the token the header slice does not already declare. The other
   four this file reads — --font, --blue, --blue-hover, --body-ink —
   come from v2-nav.css, which loads first. */
:root {
  --accent-green: #71B745;
}

/* --- bridge: v2's base rules, scoped to the hero subtree --- */
:where(.shero) {
  font-family: var(--font);
  color: #FFFFFF;
  -webkit-font-smoothing: antialiased;
}
:where(.shero) h1,
:where(.shero) p           { margin: 0; }
:where(.shero) a           { color: inherit; text-decoration: none; }
:where(.shero) img         { max-width: 100%; height: auto; display: block; }


/* ============================================================
   THE BAND
   ============================================================ */

.shero {
  position: relative;
  isolation: isolate;
  /* Shows while the photograph decodes. Two-stop, never a flat fill,
     per the dark-section rule. */
  background: linear-gradient(180deg, #131B2E 0%, #0F172A 100%);
}

/* THE PHOTOGRAPH.

   pricing-hero.jpg — a roofer dropping brick off a chimney tear-out
   into a roll-off container on the drive below. Pexels 34019842,
   Christafaari, Pexels License. Re-encoded to 1800x1412, q72, 282KB,
   which is the same scale as cta-cleanout.webp elsewhere in the
   library.

   WHY IT IS STOCK AND NOT THE CLIENT'S OWN. Every hero-scale
   photograph in /assets/img/ is either already a hero — gallery-10 on
   /dumpster/, gallery-12 on /junk-removal-page/, about-crew on
   /about/ — or carries 732-583-0378 painted legibly on the truck,
   the van door or the crew shirt, which contradicts every
   (000) 000-0000 on this page. gallery-01, gallery-04, gallery-13,
   gallery-14 and gallery-16 all fail on exactly that, and the two
   service pages had already rejected work-truck.jpg and
   service-dumpster.jpg for it. Do not re-litigate those.

   WHY THIS FRAME. It is the only free-stock frame found that shows
   debris going INTO a container: both things this page prices, in one
   picture. The alternatives were all wrong in the same two ways —
   free stock for this trade is overwhelmingly urban alley bins, and
   every roll-off that is well photographed carries its operator's
   name and number down the flank (LIBERTY, HEAVE AWAY, G. VAN DIJK
   were the three best-shot rejects).

   THE FILE IS PRE-CROPPED ON THE RIGHT AND THAT IS LOAD-BEARING. The
   source is 3936x2624; this is the left 3345 of it. The strip that
   came off held a white van with a third-party logo and phone number
   down its flank — the same objection as the client's own painted
   number, and worse, because it is somebody else's. IF YOU RE-EXPORT
   FROM THE SOURCE, CROP THE RIGHT 15% AGAIN.

   The remaining 3345x2624 is about 1.27:1, deliberately taller than
   the band. Baking the 2:1 desktop crop into the file would leave the
   phone — where the band is nearly square — nothing to crop from.

   ONLY THE Y VALUE DOES ANY WORK. The band is about 2.4:1 at a
   1920-wide window, so cover matches the WIDTH and trims the height;
   no horizontal position can change a pixel.

   22%, chosen against three alternatives rendered and compared:

     50%  the container and its debris hold the bottom right, but the
          worker is cropped at the shoulders and the headline lands on
          a headless torso
     38%  the brick and the container lip are both in frame and the
          bottom third fills with roof deck and OSB — the busiest of
          the three under the offer card
     22%  the whole figure is in frame against the maple, the road
          holds the right, and the bottom edge cuts at the tool belt

   22% wins on the type, not on the subject: it is the only one of the
   three whose centre — where the headline and lede sit — is unbroken
   shade rather than skin, shirt and shingle. The cost is real and is
   worth naming: THE CONTAINER IS OUT OF FRAME AT THIS VALUE. What is
   left reads as a crew at work rather than as a dumpster, which is
   the same trade /about/'s hero makes.

   decoding="sync" matches the other three v2 heroes. On the About
   page async decode left the band painting as the bare fallback
   gradient on first load; the note there has the detail. */
.shero__photo {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 22%;
}

/* ONE SOFT ELLIPSE, AND NOTHING ELSE — see junk-removal.css for why
   an ellipse and not a veil.

   THE ELLIPSE FOLLOWS THE TYPE. It is centred at 45%/43% rather than
   dead centre, because this hero is left-aligned and its type block
   sits left of and above the middle of the band. A scrim centred on
   the band would be putting its darkest point where there is no type.

   Measured analytically rather than by screenshot, and that is a
   change of method worth naming: the band is reconstructed from
   pricing-hero.jpg, the object-fit: cover geometry and the gradient
   above, then sampled inside the real layout boxes read off the live
   page. It is deterministic and it re-runs without a browser, where
   the screenshot-diff method the other two heroes used depends on a
   stable capture. It samples the type's BOX rather than a glyph mask,
   so it also counts the ground between letters — which is the
   stricter reading of the two.

   1568-wide viewport, band 1553 x 648:

                     no scrim        with scrim       floor
       headline     15.41 / 3.71     17.23 / 11.04      3.0   PASS
       accent        5.33 / 1.24      6.41 /  3.73      3.0   PASS
       lede         14.62 / 2.39     17.12 / 10.16      4.5   PASS
       offer card   16.98 / 12.41    18.22 / 16.50      4.5   PASS
       buttons      14.52 / 3.26     16.28 /  7.31      4.5   PASS
                    (mean / worst tenth, sampled across the type box)

   THE ACCENT IS THE TIGHT ONE and it is the only number here worth
   watching. 3.73 against a 3.0 floor is the smallest margin in the
   table, and it is there because "Dumpster Rentals" crosses the
   worker's red shirt and forearm — green type over red cloth, which
   is also the worst hue pair for a red-green deficient reader. It
   passes, the accent is decoration rather than meaning, and the
   sentence reads identically in greyscale. But if the crop, the
   picture or the rail moves, THIS is the row that fails first: at the
   scrim these values replaced it measured 2.67 and FAILED.

   THE COST WAS CHECKED. The four corners of the band go 12.8% darker
   than the bare photograph, against 7% on /dumpster/. That is the
   price of a left-aligned hero on a picture whose bright subject is
   in the middle, and it was the cheapest setting that cleared the
   accent with margin — 78%/66% at .62 left it failing, and going on
   to 90%/85% bought 0.65 more contrast for double the flattening.

   The 'offer card' row is measured through the card's own ink fill,
   which is part of the design, so both of its columns include it.

   RE-MEASURE IF THE PICTURE, THE CROP, THE RAIL OR THE BAND HEIGHT
   CHANGES. All four change which pixels sit under the type. The
   phone-width ellipse in the 860px block has its own measurements
   against its own crop — read them there, they are not derived from
   these.
   .shero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 82% 72% at 45% 43%,
                    rgba(8, 14, 28, .70)  0%,
                    rgba(8, 14, 28, .50) 55%,
                    rgba(8, 14, 28, .07) 100%);
}

/* LEFT-ALIGNED AND WIDE, WHICH IS WHERE THIS HERO PARTS COMPANY WITH
   THE OTHER THREE. They centre a 1320px column and let text-wrap:
   balance square the headline off into a block. That reads as a
   narrow spine down the middle of a very wide band, and it breaks the
   headline onto a second line at widths where it did not have to.

   The rail here is the padding, not a max-width: 5vw a side, which
   at 1920 is a 16% gutter in total and leaves the type the other 84%
   — about 1610px. That number is not arbitrary: the headline sets in
   roughly 1500px at its 60px ceiling, so this is the narrowest rail
   that still keeps it on ONE line at a common desktop width. The
   max-width only stops the measure running away on an ultrawide.

   THE BAND IS TALLER THAN THE TWO SERVICE PAGES' — 84svh against
   78svh — because this one carries a fourth block. Headline, lede,
   offer card and buttons need the room; at 78svh the buttons crowd
   the bottom edge on a 900px-tall window. */
.shero__inner {
  position: relative;
  z-index: 2;
  max-width: 1800px;
  margin: 0 auto;
  min-height: clamp(560px, 84svh, 780px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  padding: clamp(64px, 8vh, 104px) clamp(20px, 5vw, 110px) clamp(88px, 12vh, 150px);
  text-align: left;
}

/* Weight 400, not the 300 the rest of v2 carries — the scrim fixes
   the ratio but not the stroke, and thin strokes let a busy ground
   read through them. junk-removal.css has the full argument.

   NO text-wrap: balance, AND THAT IS DELIBERATE. The other three
   heroes carry it. Balance divides the headline into lines of even
   length, which on a centred block looks tidy and on a wide
   left-aligned one just pulls the first line in short of the rail and
   starts a second line that was not needed. Plain wrapping fills the
   measure and breaks only when it runs out. At 1920 this headline
   sets on one line; the break comes back on its own below about
   1500. */
.shero__title {
  font-family: var(--font);
  font-weight: 400;
  font-size: clamp(30px, 4.4vw, 60px);
  line-height: 1.06;
  letter-spacing: -.04em;
  color: #FFFFFF;
}

/* THE ACCENT LANDS ON "DUMPSTER RENTALS" BECAUSE THE LIVE PAGE PUT IT
   THERE. Webflow's <h2> wraps exactly that phrase in a <span> and
   colours it #1a56db; this keeps the phrase and changes the hue to
   v2's green, which is what /dumpster/ did with v1's marking of
   the same two words. #71B745 measures 7.0:1 on the ink fallback and
   is never asked to carry copy at body size. */
.shero__accent { color: var(--accent-green); }

/* 78ch, not the rail. The headline wants the full width; a paragraph
   does not — past about 80 characters the eye starts losing the head
   of the next line, and this lede is only two lines long. text-wrap: pretty rather than balance: it only pulls
   back orphans, it does not even the lines up. */
.shero__lede {
  margin: 18px 0 0;
  max-width: 78ch;
  font-size: clamp(16px, 1.2vw, 18px);
  font-weight: 600;
  line-height: 1.6;
  color: #FFFFFF;
  text-wrap: pretty;
}

/* ============================================================
   THE OFFER CARD

   THE ONE THING THIS HERO CARRIES THAT THE OTHER THREE DO NOT, and it
   is here on request. Live runs it as a white card floating to the
   right of the headline in a two-column hero; here it sits under the
   lede, centred, on the same axis as everything else, so the hero
   still reads as one column top to bottom.

   ITS THREE FACTS ALSO APPEAR ON EVERY SIZE CARD BELOW — "7-day
   rental included", "3 extra days free when you book now",
   "Additional days only $20/day". That is live's own duplication, not
   something introduced here, and it is the reason dropping this card
   was the other option on the table.

   THE FILL IS INK AT 55%, NOT WHITE AT 10%. A white wash over a
   photograph gives you the photograph back through the type; ink over
   it darkens toward the ground the rest of the hero already assumes,
   so the card reads as part of the scrim rather than as a hole in it.
   Measured through the fill: white copy 16.72:1 mean, 14.05:1 across
   the worst tenth — the most legible block in the band.

   NO backdrop-filter. It would be the only blurred surface on the
   page below the nav, and on a 1800px photograph the cost is a full
   compositor layer for an effect nothing else answers.
   ============================================================ */

.shero__offer {
  margin: 26px 0 0;
  max-width: 560px;
  width: 100%;
  padding: 20px 22px 22px;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 18px;
  background: rgba(11, 18, 38, .55);
}

/* 12px/800 uppercase at .12em. The same eyebrow treatment the section
   labels below use, in white rather than green — green at this size
   is under the 18.66px large-text threshold, so its floor is 4.5, and
   #71B745 does not clear that on this ground. */
.shero__kicker {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .78);
}

/* An <h2> now, not a <p> — live marks it up as a heading. Both margins
   are stated because a heading brings a UA bottom margin a <p> in this
   card did not have. */
.shero__offertitle {
  margin-top: 8px;
  margin-bottom: 0;
  font-size: clamp(19px, 1.7vw, 23px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -.02em;
  color: #FFFFFF;
}

/* THE EXPLANATORY PARAGRAPH IS BACK — read this before removing it
   again.

   It was cut on the reasoning that "Every dumpster rental includes 7
   days. After the included rental period, additional days are only $20
   per day" just says in prose what the two chips under it already say
   in figures. That reasoning is sound as design, and wrong for this
   project: the parity rule is that every word on the live page has to
   survive the rebuild, and this sentence is live copy. Cutting it also
   cost the page its only prose statement of the $20/day figure — chips
   are two words each, and a sentence is what a search engine can read
   as a claim about the offer.

   The clutter concern is answered by weight rather than by deletion:
   it sits at 14px/1.5 in 78% white, below the 19-23px title and above
   the chips, so it reads as supporting text rather than a second
   headline. See v2/_audit/pricing.md. */
.shero__offercopy {
  margin-top: 8px;
  font-size: clamp(13px, 1.05vw, 14px);
  line-height: 1.5;
  color: rgba(255, 255, 255, .78);
}

.shero__chips {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-top: 16px;
}

/* THE TWO CHIP COLOURS ARE THE BUTTON COLOURS, and their ink is set
   by measurement rather than by matching each other:

     #FFFFFF on #2F6FE5 .... 4.64   PASS
     #FFFFFF on #71B745 .... 2.45   FAILS the 4.5 floor
     #0B1226 on #71B745 .... 8.06   PASS

   Live sets both chips' text to white. The green one at 2.45:1 is not
   a near miss, so the green chip takes ink instead. This is the same
   pair, and the same reasoning, as .btn--solid and the green button
   in junk-removal.css.

   BOTH LINES IN A CHIP TAKE THE FULL COLOUR. The figure and its label
   are the same ink at the same alpha; see the note on
   .shero__chiplabel for why the label is not dimmed. 4.64:1 is the
   thinnest ratio in this hero and it is the blue chip's, so if the
   blue ever moves, this is what to re-check. */
.shero__chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 14px 12px;
  border-radius: 12px;
  text-align: center;
}
.shero__chip--blue  { background: var(--blue); color: #FFFFFF; }
.shero__chip--green { background: var(--accent-green); color: #0B1226; }

.shero__chipvalue {
  font-size: clamp(17px, 1.5vw, 20px);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -.02em;
}
/* NO OPACITY ON THIS LINE, AND THAT IS THE WHOLE NOTE. It was
   white at 88% to sit the label under the figure, which blends to
   about #E6EEFC on the blue chip and lands at 3.98:1 — under the 4.5
   floor for 11.5px copy. The hierarchy was never coming from the
   opacity anyway: 11.5/600 against 20/800 is already two steps apart
   on size and one on weight. Full ink, and the ratios above hold. */
.shero__chiplabel {
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.3;
}

.shero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 28px;
}


/* ============================================================
   BUTTONS — the v2 pair, scoped to the hero

   Copied from junk-removal.css: 48px tall, 34px sides, fully round,
   16/700. SCOPED TO .shero ON PURPOSE. `.btn` is unused by Webflow
   today, but this page is a mirror and a future re-capture could
   introduce one; scoping means a collision costs nothing.

   Contrast:
     #FFFFFF on #2F6FE5 .... 4.64   solid, computed on the fill
     #FFFFFF on the scrim . 12.95   ghost label, measured on the page
     #0B1226 on #FFFFFF ... 18.42   ghost, hovered
   ============================================================ */

.shero .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;             /* clears the 44px touch minimum */
  padding: 0 34px;
  border: 1px solid transparent;
  border-radius: 100px;
  font-family: var(--font);
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  text-align: center;
  text-decoration: none;
  transition: background-color .2s linear, border-color .2s linear, color .2s linear;
}

.shero .btn--solid {
  background: var(--blue);
  border-color: var(--blue);
  color: #FFFFFF;
}
.shero .btn--solid:hover { background: var(--blue-hover); border-color: var(--blue-hover); }

/* The outlined button on a dark ground. A navy outline on navy is
   invisible, so the border and label invert and the hover fills solid
   white rather than solid navy. */
.shero .btn--ghost-light {
  background: transparent;
  border-color: rgba(255, 255, 255, .55);
  color: #FFFFFF;
}
.shero .btn--ghost-light:hover { background: #FFFFFF; border-color: #FFFFFF; color: #0B1226; }


/* ============================================================
   RESPONSIVE — the two service heroes' breakpoints, plus the card
   ============================================================ */

@media (max-width: 860px) {
  .shero__inner {
    min-height: clamp(500px, 72svh, 640px);
    padding: 52px 20px 68px;
  }
  /* The offer card stops being a card-width object and takes the rail,
     because at this width the rail IS card width. */
  .shero__offer { max-width: none; }
  /* THE PHONE ELLIPSE IS WIDER *AND* DARKER, and unlike the earlier
     version of this rule it has now been measured.

     The phone case is not a smaller version of the desktop one, it is
     a different crop. At 390x640 the band is TALLER than it is wide,
     so cover matches the HEIGHT instead of the width and shows only
     the middle 48% of the photograph — the worker's lit shirt and the
     tree, with none of the dark foliage that carries the desktop
     crop. Measured at 430x695, which is the band's real geometry read
     off the rendered page, sampled across each block's box:

                      no scrim      106%/80% .62     this one
         headline    16.12 / 4.00   17.23 / 9.07   17.81 / 11.92
         lede        12.78 / 2.13   15.23 / 6.72   16.34 /  9.67
         buttons      4.70 / 1.17    8.57 / 4.17   10.87 /  6.70

     THE BUTTON ROW IS WHY THIS CHANGED. It sits near the bottom edge
     where the old ellipse had already fallen to .10. Its 4.17 is also
     misleading on its own: that figure is for SOLID white, and the
     Call button was not solid white — it was a 55% white hairline
     with nothing behind it, so its real edge contrast was roughly
     half the number in the table. Widening the ellipse alone would
     not have fixed it, which is why the fill below exists too. */
  .shero__scrim {
    background:
      radial-gradient(ellipse 116% 92% at 50% 50%,
                      rgba(8, 14, 28, .74)  0%,
                      rgba(8, 14, 28, .58) 60%,
                      rgba(8, 14, 28, .24) 100%);
  }

  /* AND THE OUTLINED BUTTON GETS A FILL, phone only. On desktop it is
     a hairline on a dark ellipse and reads fine; at this width it was
     a 55%-white outline over the brightest part of the picture, which
     is a button you have to hunt for. A translucent ink fill plus a
     brighter edge makes it a button again without making it a second
     solid one competing with the blue. Desktop is untouched. */
  .shero .btn--ghost-light {
    background: rgba(11, 18, 38, .55);
    border-color: rgba(255, 255, 255, .85);
  }
}

@media (max-width: 560px) {
  .shero__inner { padding: 44px 18px 56px; }
  .shero__lede  { max-width: none; }
  /* The headline gets balance back on a phone and only there. At
     this measure it sets on four or five lines whatever happens, so
     the argument against balancing — that it starts a line that was
     not needed — no longer applies, and an even block reads better
     than a long line over a two-word orphan. */
  /* 500, not the 400 the band uses everywhere else. Asked for, and
     the measurement backs it: at this width the headline drops to its
     30px floor over the busiest part of the photograph, and a 30px
     stroke at 400 has far less ink holding the letterform than the
     same weight at 60px does. One step, phone only — 600 starts to
     read as a different headline from the desktop one. */
  .shero__title { text-wrap: balance; font-weight: 500; }
  .shero__offer { padding: 20px 18px 22px; }
  /* Stacked. Two chips side by side in a 303px card leave each about
     145px, and "After Included Period" then breaks onto three lines. */
  .shero__chips { grid-template-columns: 1fr; gap: 10px; }
  /* Full width and stacked, same as the other heroes. Two pills side
     by side in a 339px column leave each about 163px, under the
     comfortable tap width. */
  .shero__actions { gap: 10px; width: 100%; }
  .shero .btn { flex: 1 1 100%; }
}

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