/* ══════════════════════════════════════════════════════════════════════════
   banner-v2.css — the article banner. Extracted from article.css 7 Aug 2026.

   🔴 WHY ITS OWN FILE: the review pages (review-*.html) carry their own inline
   <style> and link only main.css — they do NOT link article.css. Linking the whole
   of article.css into them collides on 53 selectors including :root, .article-body
   and the whole sidebar, restyling far more than the banner. Every selector here is
   scoped to .banner-v2, so this file is safe to drop into any page.

   Load it AFTER main.css. `.article-banner.banner-v2` is specificity (0,2,0) and
   beats a page's own `.article-banner` (0,1,0) regardless of source order — which
   is what lets a converted page keep its existing inline banner rules harmlessly.
   ══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
   BANNER v2 — art-only SVG + real HTML text on top.        Added 7 Aug 2026.

   Why: the old banners baked the headline into the image, so (a) any container
   whose aspect differed from the art's 2.4:1 cropped words mid-glyph, and
   (b) every article's <h1> was pixels — invisible to search, screen readers,
   translation and selection.

   Now: the SVG carries only abstract art and may crop freely. Every word is
   HTML. clamp() scales type continuously (1080p -> 8K); the @container query
   sheds optional content when the column gets narrow.

   🔴 CONTAINER query, not @media. A media query keys to the viewport, so two
   banners of different shapes on one page would render identically.

   🔴 The banner NEVER has a fixed height - see the min-height note below. Nothing
   here may clip, so no rule in this file should reintroduce `height:` on the
   container. (Superseded 7 Aug: this file previously used container-type:size and
   max-height queries. That required a definite height, which is exactly what
   caused the clipping. It is now inline-size.)

   Pages using this set  class="article-banner banner-v2"  and put the header
   content inside it; the separate .article-header title block is dropped so
   the headline appears once, not twice.
   ══════════════════════════════════════════════════════════════════════════ */

/* 🔴 min-height, NEVER height. Fixed height was the clipping bug of 7 Aug: once the
   headline wrapped to four lines the content stack was taller than the box, and
   align-content:center overflowed it in BOTH directions - up under the fixed navbar
   and down over the article header. A banner that cannot grow will always find a
   headline long enough to break it. The floor keeps short banners generous; the
   content sets the ceiling. This is the whole "let it breathe" requirement. */
/* 🔴 position:relative and overflow:hidden MUST be declared here, not assumed.
   .banner-art is absolutely positioned; without a positioned ancestor it resolves
   against whatever is further up the tree, escapes the banner entirely and paints
   down the page - and .article-banner's own overflow:hidden does NOT clip it,
   because the banner is not its containing block. Every page except
   future-proof-your-career.html happened to set position:relative in its own CSS,
   which hid this dependency for 35 conversions. */
.article-banner.banner-v2{container-type:inline-size;container-name:banner;
  position:relative;overflow:hidden;
  min-height:clamp(340px,52vh,620px);height:auto;aspect-ratio:auto;max-height:none;
  display:grid;align-items:center;
  /* 🔴 Clear the navbar with PADDING, not margin. nav has no fixed height - it is
     padding:1.25rem plus its content, so it measures ~71-75px, while the pages
     reserve margin-top:65px. The banner top therefore sat behind it and ate the
     kicker. Padding also lets the artwork run full-bleed up behind the translucent
     nav, which reads better than a band of page background above it. */
  margin-top:0;
  padding-top:clamp(96px,10vh,140px);
  padding-bottom:clamp(28px,4.5vh,72px)}

/* 🔴 Undo the header's pull-up. Article pages set .article-header{margin:-5.5rem auto 0}
   (-4rem on mobile) to lift the header into the old banner's bottom fade. That is fine
   over a picture, but over v2 it drags the breadcrumb and meta card straight through the
   dek and tags. Adjacent-sibling wins on specificity (0,2,0) in every breakpoint, and
   ignores the HTML comment between the two elements. */
.article-banner.banner-v2 + .article-header{margin-top:clamp(1.75rem,3vh,3rem)}

/* 🔴 Pages whose nav is STICKY, not fixed. The padding-top above reserves room for a
   fixed navbar that overlaps content; where the nav participates in layout instead,
   that reservation is just dead space at the top of the banner. Add `nav-static`
   alongside `banner-v2`. (future-proof-your-career.html is the one such page.) */
/* (0,3,0) so it cannot lose to a page rule, and restates every box property.
   future-proof-your-career.html sets `.article-banner{aspect-ratio:1200/500}`; at
   full-bleed width that computes to a ~1065px tall box, which is exactly the height
   its banner was rendering. Restating here removes any doubt about which rule wins
   and costs nothing where the page has no such rule. */
.article-banner.banner-v2.nav-static{
  padding-top:clamp(24px,4vh,56px);
  aspect-ratio:auto;height:auto;max-height:none;
  min-height:clamp(320px,44vh,560px);
  border:0;border-radius:0;margin-bottom:0}
/* the artwork must never contribute height - if it is ever left in flow it renders
   at its intrinsic 2.4:1, which is the same ~1065px */
.article-banner.banner-v2 > img.banner-art{position:absolute}
@media (max-width:768px){
  .article-banner.banner-v2.nav-static{padding-top:clamp(18px,3vh,32px)}
}
/* 🔴 The bottom fade must sit BELOW the text. `.article-banner::after` (the
   transparent->bg blend every article page defines) and `.banner-inner` were both
   z-index:2, so source order decided and the pseudo-element - generated last -
   painted straight over the dek, meta and tags, greying them out. Looked like
   clipping on mobile; it was compositing. Fade pinned to 1, content lifted to 3. */
.article-banner.banner-v2::after{height:38%;z-index:1}

/* 🔴 opacity + blend reset. 9 pages carry the older banner markup, whose inline CSS
   says `.article-banner img{opacity:.4;mix-blend-mode:luminosity}` - a treatment for
   a decorative photo behind a separate header. Those are different properties from
   the ones set here, so specificity never comes into it: without an explicit reset
   they simply survive and desaturate the v2 artwork to 40%. */
.banner-v2 .banner-art{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:center;z-index:0;
  opacity:1;mix-blend-mode:normal;filter:none}
/* Centred wash, not a left-to-right ramp. Once the text is capped at 1400px, a
   directional ramp puts its darkest point at the far left where there is no longer
   any text, washing out the right-hand panel. A radial keyed to the centre darkens
   evenly behind the content and EASES OUT at the flanks — so the full-bleed artwork
   is actually visible either side instead of being crushed, which is also what stops
   the capped measure reading as a box. */
.banner-v2 .banner-veil{position:absolute;inset:0;z-index:1;background:
  radial-gradient(125% 145% at 50% 50%, rgba(8,11,16,.86) 0%, rgba(8,11,16,.74) 45%,
                  rgba(8,11,16,.40) 80%, rgba(8,11,16,.26) 100%)}

/* The ART stays full-bleed; only the TEXT is capped. At 2560px an uncapped measure
   put the two blocks 2417px apart — the eye lands centre and catches neither. Capping
   the inner to 1400px brings the spread to ~1272px, still left-justified, symmetric
   margins by construction rather than by a hand-tuned padding value. */
/* In normal flow (position:relative), NOT absolute - that is what lets the banner
   grow to fit. Absolute positioning took the text out of flow, so the container had
   no idea how tall its own content was. Slightly wider left column + balance so the
   headline breaks evenly instead of stranding a word. */
/* 🔴 min-width:0 on the CHILDREN, not just the container. A grid item defaults to
   min-width:auto, i.e. it refuses to shrink below its min-content width - the longest
   unbreakable word. `overflow-wrap:break-word` does NOT reduce min-content size (per
   spec), so the item still expanded and pushed past the banner's right edge even
   though the text itself wrapped. Found by banner-qa 8 Aug: 39 pages, and EVERY clip
   reported the unnamed wrapper DIV, never the title - which is what pointed here.
   Worst offenders clipped at 100% with no text scaling at all. */
.banner-v2 .banner-inner > *{min-width:0}
.banner-v2 .banner-inner{position:relative;z-index:3;display:grid;width:100%;
  overflow-wrap:break-word;
  grid-template-columns:minmax(0,1.12fr) minmax(0,.88fr);gap:clamp(12px,3vw,54px);
  align-items:center;padding:0 clamp(18px,4vw,64px);
  max-width:1400px;margin-inline:auto}

.banner-v2 .banner-kicker{font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.56rem,.72vw,.76rem);letter-spacing:.34em;text-transform:uppercase;
  color:var(--art-accent,#00e5ff);margin:0 0 clamp(6px,1vw,14px)}

/* 🔴 Long words must be allowed to BREAK. Reported 8 Aug from an iPhone with
   Safari's text-size setting raised: "AI Hallucinations:" ran off the right edge.
   The clamp's floor is in rem, so a larger text setting scales it past what the
   column can hold, and a single unbreakable word then overflows rather than wraps.
   Do NOT "fix" this with -webkit-text-size-adjust - that overrides a deliberate
   accessibility choice. Let the word break instead; hyphens:auto makes the break
   read as typography rather than as damage (needs lang on <html>, which we have). */
.banner-v2 .banner-title{font-family:var(--font-display,Syne,sans-serif);font-weight:800;
  letter-spacing:-.025em;line-height:1.02;font-size:clamp(1.5rem,3.6vw,4rem);
  margin:0;color:#fff;text-wrap:balance;
  overflow-wrap:anywhere;hyphens:auto;min-width:0}
/* 🔴 Long single words set the ceiling, not the average headline. Measured 8 Aug from
   user-expectations: Syne 800 runs ~0.87em per character, so "Understanding" at 4rem
   needs ~722px against a ~700px left column - it did not fit, break-word fired, and
   the word split with NO hyphen because text-wrap:balance suppresses hyphenation on
   that path. The result reads as damage. Dropping the cap one notch and widening the
   column slightly buys ~90px, which clears every headline currently on the site. */
.banner-v2 .banner-title{font-size:clamp(1.5rem,3.4vw,3.6rem)}
.banner-v2 .banner-inner{grid-template-columns:minmax(0,1.18fr) minmax(0,.82fr)}
/* Two distinct jobs, deliberately separated. The site's h1s use <em> for an INLINE
   emphasised word ("A Review of <em>Grammarly</em>:") and <br> for the line break.
   Treating <em> as the outlined second line — as the first draft did — turned single
   words into block-level outlines. So:
     em    = inline, accent-coloured, still in the flow
     .l2   = the outlined second line (was <em> on the story page) */
.banner-v2 .banner-title em{font-style:normal;color:var(--art-accent,#00e5ff)}
.banner-v2 .banner-title .l2{display:block;color:transparent;
  -webkit-text-stroke:1.2px var(--art-accent,#00e5ff)}

.banner-v2 .banner-dek{font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.62rem,.92vw,.9rem);line-height:1.75;color:var(--muted,#4a6a88);
  margin:clamp(8px,1.4vw,18px) 0 0;max-width:36ch}
.banner-v2 .banner-meta{font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.55rem,.72vw,.74rem);color:var(--muted,#4a6a88);opacity:.75;
  margin:clamp(8px,1.2vw,16px) 0 0}
.banner-v2 .banner-tags{display:flex;flex-wrap:wrap;gap:8px;margin-top:clamp(8px,1.2vw,16px)}
.banner-v2 .banner-tags span{font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.5rem,.62vw,.64rem);letter-spacing:.12em;text-transform:uppercase;
  padding:.32em .7em;border:1px solid var(--art-accent-soft,rgba(0,229,255,.45));
  color:var(--art-accent-soft,#00e5ff)}

/* right-hand data panel — used where the artwork was a list/table rather than a diagram */
.banner-v2 .banner-panel{align-self:center;width:100%;min-width:0}
.banner-v2 .panel-head{font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.6rem,.82vw,.92rem);letter-spacing:.26em;text-transform:uppercase;
  color:var(--art-accent,#00e5ff);text-align:center;margin:0 0 clamp(8px,1.2vw,18px)}
.banner-v2 .panel-row{display:grid;grid-template-columns:minmax(5.2ch,auto) 1fr;
  gap:clamp(12px,1.8vw,26px);align-items:baseline;padding:clamp(6px,.95vw,15px) 0;
  border-bottom:1px solid var(--border,#1e2d3d)}
/* The VALUE carries the column. Display face, not mono at body weight - that swap
   is most of the visual gain. Measured 7 Aug: the old value rendered 13.4px against
   a 70px headline at 1920px (19%), so the panel read as a footnote instead of as the
   other half of the banner. Now ~45%. Its own fixed column keeps the figures on a
   common left edge instead of ragging against variable label lengths. */
.banner-v2 .panel-row b{font-family:var(--font-display,Syne,sans-serif);font-weight:700;
  overflow-wrap:break-word;min-width:0;
  font-size:clamp(1.05rem,1.75vw,2rem);line-height:1.1;letter-spacing:-.02em;
  color:var(--art-accent,#00e5ff)}
.banner-v2 .panel-row span{min-width:0;overflow-wrap:anywhere;
  font-family:var(--font-mono,'Space Mono',monospace);
  color:var(--text,#c8d8e8);font-size:clamp(.68rem,.95vw,1.02rem);line-height:1.45}
.banner-v2 .panel-foot{font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.56rem,.76vw,.82rem);letter-spacing:.1em;color:#a08cc0;
  margin:clamp(10px,1.4vw,20px) 0 0}
.banner-v2 .panel-foot i{display:block;font-style:normal;color:var(--text,#c8d8e8);
  margin-top:4px;letter-spacing:0}

/* ══════════════════════════════════════════════════════════════════════════
   PANEL PATTERNS

   .panel-row (above) is a key/value pair, and it carried all five reviews because
   their artwork WAS a table of figures. The other 21 pages are concept diagrams -
   a two-way contrast, a decision flow, a map - and forcing those into key/value
   loses the shape that made them worth drawing.

   🔴 Decided 8 Aug: a diagram either becomes REAL CONTENT here, or the background
   gets redrawn to do a different job. What it must never do is stay in the artwork
   at reduced opacity - out of focus it is a distraction that adds nothing, which is
   worse than either alternative.

   All three patterns live in .banner-panel and inherit its shed behaviour.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── SPLIT: two things set against each other (tutor vs ghostwriter, before vs
   after, promise vs practice). The rule between them is the whole point. ── */
.banner-v2 .panel-split{display:grid;grid-template-columns:1fr 1fr;
  gap:clamp(10px,1.6vw,26px)}
.banner-v2 .split-col{min-width:0;overflow-wrap:anywhere}
.banner-v2 .split-col + .split-col{padding-left:clamp(10px,1.6vw,26px);
  border-left:1px solid var(--border,#1e2d3d)}
.banner-v2 .split-head{font-family:var(--font-display,Syne,sans-serif);font-weight:700;
  overflow-wrap:break-word;
  font-size:clamp(.92rem,1.45vw,1.6rem);line-height:1.1;letter-spacing:-.015em;
  color:var(--art-accent,#00e5ff);margin:0 0 clamp(6px,.9vw,12px)}
.banner-v2 .split-col.is-warn .split-head{color:var(--art-warn,#ff6b6b)}
.banner-v2 .split-steps{font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.62rem,.86vw,.92rem);line-height:1.7;color:var(--text,#c8d8e8);margin:0}
.banner-v2 .split-out{font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.58rem,.78vw,.84rem);line-height:1.6;color:var(--muted,#4a6a88);
  margin:clamp(6px,.9vw,12px) 0 0;padding-top:clamp(6px,.9vw,12px);
  border-top:1px solid var(--border,#1e2d3d)}

/* ── FLOW: an ordered sequence (application → automated decision → denial →
   appeal). Reads top-down; the connector is drawn, not typed, so it never
   becomes a stray arrow glyph in a screen reader. ── */
.banner-v2 .panel-flow{display:grid;gap:0}
.banner-v2 .flow-step{position:relative;display:grid;
  grid-template-columns:auto minmax(0,1fr);gap:clamp(10px,1.5vw,20px);
  align-items:start;padding:clamp(5px,.8vw,11px) 0}
.banner-v2 .flow-step::before{content:"";position:absolute;left:calc(clamp(9px,.72vw,12px) - 1px);
  top:clamp(18px,2.4vw,32px);bottom:calc(-1 * clamp(5px,.8vw,11px));
  width:1px;background:var(--border,#1e2d3d)}
.banner-v2 .flow-step:last-of-type::before{display:none}
.banner-v2 .flow-dot{width:clamp(16px,1.4vw,22px);height:clamp(16px,1.4vw,22px);
  border:1px solid var(--art-accent,#00e5ff);border-radius:50%;display:grid;
  place-items:center;font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.46rem,.58vw,.62rem);color:var(--art-accent,#00e5ff);
  background:rgba(8,11,16,.6)}
.banner-v2 .flow-step.is-end .flow-dot{border-color:var(--art-warn,#ff6b6b);
  color:var(--art-warn,#ff6b6b)}
.banner-v2 .flow-label{min-width:0;overflow-wrap:anywhere;
  font-family:var(--font-mono,'Space Mono',monospace);
  font-size:clamp(.62rem,.88vw,.94rem);line-height:1.5;color:var(--text,#c8d8e8)}
.banner-v2 .flow-label b{display:block;font-family:var(--font-display,Syne,sans-serif);
  font-weight:700;font-size:clamp(.72rem,1vw,1.06rem);letter-spacing:.01em;
  color:var(--art-accent,#00e5ff);margin-bottom:2px}
.banner-v2 .flow-step.is-end .flow-label b{color:var(--art-warn,#ff6b6b)}

/* shed the same way .panel-row does */
@container banner (max-width: 1080px){
  .banner-v2 .split-out{display:none}
  .banner-v2 .flow-step:nth-of-type(n+5){display:none}
}

/* Shed by HEIGHT, not aspect ratio. Measured 7 Aug: with a 52vh banner the aspect
   is 3-4.5:1 on essentially every desktop, so an aspect-ratio breakpoint fired
   constantly and hid the dek and tags everywhere. Vertical room is the real
   constraint - "is there space for six rows?" - so query that directly. */
/* Shed by INLINE-SIZE. container-type:size needs a definite height in BOTH axes;
   now that height is content-driven it would resolve to nothing and the queries
   would never fire. Height-based shedding also stopped being the point: nothing
   can be clipped any more, so these exist to keep the measure readable, not to
   rescue overflow. */
@container banner (max-width: 1080px){
  .banner-v2 .panel-row:nth-of-type(n+4){display:none}
  .banner-v2 .banner-dek{display:none}
}
@container banner (max-width: 820px){
  .banner-v2 .banner-panel{display:none}
  .banner-v2 .banner-inner{grid-template-columns:1fr}
  .banner-v2 .banner-tags{display:none}
}
/* ⚠️ Never use display:revert to undo these - it reverts to the UA default
   (block), not to .panel-row's grid, which silently drops the column gap. */

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE
   ⚠️ Every property the desktop rule sets must be restated here. The desktop
   `.article-banner.banner-v2` is (0,2,0) and the pages' own mobile
   `.article-banner` is (0,1,0) - specificity beats source order ACROSS media
   queries, so anything omitted leaks down from desktop.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width:768px){
  /* A LOW floor on phones. min-height is insurance against a near-empty banner,
     not a target - set too high it becomes dead space, because align-items:center
     floats short content in the middle of an oversized box. story-daily-ai (kicker
     + 2-line headline + meta, ~200px) left ~170px of void under it at 44vh. Let
     the content set the height; the floor only catches the degenerate case. */
  .article-banner.banner-v2{aspect-ratio:auto;max-height:none;
    min-height:clamp(200px,30vh,300px);height:auto;   /* min-height, never height */
    padding-top:clamp(80px,11.5vh,100px);
    padding-bottom:clamp(20px,3vh,36px)}
  .banner-v2 .banner-inner{grid-template-columns:1fr;max-width:none;
    padding:0 1.25rem}
  .banner-v2 .banner-panel{display:none}
  /* single column, so a centred radial has nothing to centre on - use a vertical
     ramp that lands its weight where the text actually is */
  .banner-v2 .banner-veil{background:
    linear-gradient(180deg,rgba(8,11,16,.62) 0%,rgba(8,11,16,.86) 55%,rgba(8,11,16,.92) 100%)}
  /* the desktop clamp bottoms out at 1.5rem on a 375px screen, which is small for
     the only headline on the page; give the phone its own range */
  .banner-v2 .banner-title{font-size:clamp(1.85rem,8.2vw,2.7rem)}
  .banner-v2 .banner-dek{max-width:none;font-size:clamp(.72rem,3.4vw,.86rem)}
  .banner-v2 .banner-kicker{font-size:clamp(.6rem,2.8vw,.72rem);letter-spacing:.24em}
  .banner-v2 .banner-meta{font-size:clamp(.62rem,2.9vw,.74rem)}
  .banner-v2 .banner-tags{display:flex}
  .article-banner.banner-v2 + .article-header{margin-top:1.25rem}
}
