/* =====================================================================
   QUOTE BLOCK — portrait, pull quote, attribution
   THE TREATMENT IS SITE-WIDE: it is scoped to .band-slate, so it reaches
   every quote on the site. The homepage block carries a portrait, the
   utilities block does not, and the layout must read as finished either
   way — see "DEGRADES WITH NO PORTRAIT" below.
   ===================================================================== */

/* ---------------------------------------------------------------------
   TEXTURE OVERLAY

   Same shape as the homepage's problem band, and it is on a ::before for
   the reason measured there: mask-image on the SECTION masks the section
   AS A GROUP, ground and text together — 62.65% of band pixels changed,
   mean RGB 71 -> 177, headline shredded, and getComputedStyle reported
   maskImage:none throughout, so computed style could not see it. The
   overlay carries the mask; the copy rides above it and is never masked.

   THE PAINT TOKEN IS INVERTED FROM THE HOMEPAGE'S, AND THAT IS FORCED BY
   THE GROUND. The problem band paints --slate onto --ground-dark, which
   LIGHTENS. This band's ground already IS --slate and carries LIGHT text,
   so a lightening paint eats the text's headroom. Measured at .10:
     --ink   darkens -> inked ground rgb(89,108,124)  white 5.4336
     --stone lightens -> rgb(103,122,136)             white 4.4447  FAILS
     --cream lightens -> rgb(109,128,142)             white 4.0771  FAILS
   against a bare-slate white of 4.8755 and a 4.5 threshold. --ink is the
   only candidate that does not spend contrast to add texture.

   WHICH IS WHY THE OPACITY SWEEP BINDS DIFFERENTLY HERE. A mask makes the
   ground a RANGE, not one colour: fully-inked at one extreme, bare ground
   at the other. For LIGHT text the worst case is the LIGHTEST ground —
   which is the bare band, and is INVARIANT under this overlay. Raising
   the opacity moves the inked end away from the threshold, never toward
   it. On the problem band the opposite was true and .20 failed. DO NOT
   CARRY A NUMBER BETWEEN THE TWO BANDS IN EITHER DIRECTION.

   Opacity here is therefore a legibility-of-texture decision, not a
   contrast one, and it is set where the lattice reads as tone rather than
   as pattern competing with the type.

   NO TILING. The source's motif period is non-integer (autocorrelation
   peak r=0.936 at lag 242; 14,746/242 = 60.93), so no crop tiles cleanly
   at any size. mask-size:cover on one non-repeating region has no seam at
   any viewport. Do not add mask-repeat:repeat.

   ZERO COLOUR LITERALS. The PNG is a MASK: its alpha decides WHERE paint
   lands, an existing token decides WHAT the paint is. The image
   contributes no colour to the build.
   --------------------------------------------------------------------- */
.band-slate{position:relative}
.band-slate::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:var(--ink);
  -webkit-mask-image:url(../../assets/lh-texture-lattice.png);
          mask-image:url(../../assets/lh-texture-lattice.png);
  -webkit-mask-size:cover;         mask-size:cover;
  -webkit-mask-repeat:no-repeat;   mask-repeat:no-repeat;
  -webkit-mask-position:center;    mask-position:center;
  opacity:.14;
}
/* The content rides above the overlay. WITHOUT THIS the ::before paints
   over the quote — it is a later sibling in paint order. */
.band-slate > .in{position:relative;z-index:1}

/* ---------------------------------------------------------------------
   PORTRAIT — enlarged, ring removed

   assets/lh-portrait-mccanna.jpg DEPICTS A REAL, NAMED PERSON — Pete
   McCanna, Board Chair. It is a CLIENT-SUPPLIED PHOTOGRAPH ONLY and is
   NEVER AI-generated. It is never used as a style, reference,
   frame-capture, interpolation or synthesis input to any generation step.
   It is NEVER AI-upscaled, AI-enhanced, or run through any generative or
   ML resampler. Mechanical crop and resize only. APPROVAL TO USE IS NOT
   PERMISSION TO PROCESS. It was copied into this build byte-identically
   and is hashed, never opened.

   THE ENLARGEMENT IS A DISPLAY-SIZE CHANGE ONLY AND WAS SIZE-CHECKED
   AGAINST THE NATIVE FILE FIRST. Native is 500x500. The old rendered box
   was 106x106 (a 150px circle less 20px padding and a 2px ring). 220
   needs 220 backing pixels at DPR 1 and 440 at DPR 2, both inside 500.
   IF A LATER REVISION WANTS MORE THAN 250, STOP: at DPR 2 that exceeds
   the native file, and the answer is to request a higher-resolution
   original from the client, never to upscale.

   THE 2px DASHED RING IS REMOVED. --portrait-ring now has no consumer.
   It is deliberately NOT deleted from tokens.css — whether the palette
   keeps, retires or reassigns a token is a palette-session decision, and
   an unreferenced token costs nothing (same reasoning as --pill). */
.portrait{
  position:relative;
  flex:0 0 220px;width:220px;height:220px;
  border-radius:50%;
  overflow:hidden;
  display:flex;align-items:center;justify-content:center;text-align:center;
  font-family:var(--mono);font-size:8px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--portrait-label);
  padding:20px;
}
/* ABSOLUTE, SO THE PADDING INSETS ONLY THE FALLBACK TEXT. An absolutely
   positioned child resolves inset:0 against the containing block's
   PADDING box, so the photograph fills the circle edge to edge while the
   20px padding still keeps the onerror text off the rim. In flow the same
   img would have been sized to the content box and lost 40px. */
.portrait img{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
}

/* PORTRAIT FALLBACK WASH — PLACEHOLDER ONLY, NEVER A PERSON.
   Deliberately non-figurative: a diagonal hatch over an off-axis tonal
   wash. No face, no silhouette, no figure, no head-and-shoulders mass. It
   reads as weight-in-waiting, not as a portrait. It renders only if the
   client photograph fails to load. */
.band-slate .portrait{
  background-color:var(--fpo-base);
  background-image:
    repeating-linear-gradient(135deg,var(--fpo-hatch) 0 5px,transparent 5px 11px),
    radial-gradient(88% 76% at 30% 22%,var(--fpo-wash-hi),transparent 64%),
    radial-gradient(104% 92% at 76% 86%,var(--fpo-wash-lo),transparent 70%),
    linear-gradient(166deg,var(--fpo-hi) 0%,var(--fpo-lo) 100%);
  box-shadow:inset 0 0 22px var(--fpo-shadow);
}

/* ---------------------------------------------------------------------
   TYPE

   THE THRESHOLD DOES NOT MOVE WITH THIS CHANGE, AND THAT IS WORTH STATING
   RATHER THAN ASSUMING. The old size resolved to 32px at 1440 and the new
   one to 40px; both are >= 24px, so both are LARGE TEXT and both are
   judged at 3.0, not 4.5. The clamp floor moved 21px -> 24px, which is
   what keeps that true at the narrow end — at 21px this element would
   have been small text against a 4.5 threshold.
   --------------------------------------------------------------------- */
blockquote{
  font-family:var(--serif);
  font-size:clamp(24px,3.2vw,40px);
  line-height:1.22;font-weight:700;max-width:26ch;
}
blockquote::before{
  content:"\201C";display:block;font-size:96px;line-height:.45;
  color:var(--gold);margin-bottom:18px;
}

/* DOCUMENTED PALETTE EXCEPTION, CARRIED FORWARD DELIBERATELY: the
   attribution is set in --white rather than --on-slate. Cream on the
   slate band measures 4.3009, below AA for small text; white reaches
   4.8755. This is a decision, not an error, and it must be RE-MEASURED
   if --slate moves — the exception exists because of a specific ratio,
   not as a style preference.

   THE TEXTURE DOES NOT RELAX IT. The overlay darkens, so it can only
   raise this ratio; the binding case stays the bare, un-inked ground at
   4.8755 with 0.3755 of headroom. This is the tightest element on the
   band at every opacity. */
.attrib{
  display:block;margin-top:26px;
  font-family:var(--mono);font-size:10.5px;
  letter-spacing:.14em;text-transform:uppercase;
  font-style:normal;
  color:var(--white);
}

/* DEGRADES WITH NO PORTRAIT. The utilities block is text-only by ruling —
   no img, figure, picture or svg, and no asset exists for its attributed
   speaker. .band-slate .in is a flex row, so with a single child the quote
   column simply occupies the row; nothing here reserves a portrait slot,
   draws a placeholder frame, or leaves a gap where one would have been. */
@media(max-width:760px){
  .portrait{flex-basis:170px;width:170px;height:170px}
}

/* MOVED HERE FROM pages/home.css BY R6 (2026-08-14), rules and commentary
   together. THE MASK PATH SURVIVED THE MOVE UNCHANGED and that was checked,
   not assumed: pages/ and components/ sit at the same depth under src/css/,
   so `../../assets/` resolves to src/assets from both. A broken mask does
   not error — the texture would simply stop painting at 0.10 opacity on
   slate, invisible in a screenshot and in the console. */

/* ---------------- QUOTE BAND — THE .quote-feature MODIFIER ----------

   THE 8p CHANGE, NOW LIVING IN THE SHEET IT MODIFIES. This block and its
   rules were written in pages/home.css to avoid opening this file while
   the homepage was the only consumer; R6 (2026-08-14) moved them here when
   a second page adopted the band. The base `.band-slate::before` above is
   UNSCOPED and this file is linked by several pages — which is exactly why
   the modifier below is compounded rather than loose.

   SCOPE IS THE SELECTOR, NOT A PAGE ROSTER. These rules apply wherever
   `.band-slate.quote-feature` appears; the class is set in
   partials/components/quote-block.html, so any page including that partial
   is styled and nothing else is. The header here read "HOMEPAGE ONLY" until
   R6 and went stale the day a second page adopted it. DO NOT REINTRODUCE A
   ROSTER — it is stale the moment the next page adopts it. Count consumers
   on <link> TAGS and on the DOM, never by grepping the filename: it appears
   in comments on pages that do not link it at all (our-team.html names it
   ONCE, at :21, and links it zero times — the deliberate trap, still set).
   THE COUNT WAS "twice" UNTIL S3 RE-MEASURED IT, 2026-08-15. The second
   naming sat inside the Paul Mango memorial, which left for our-origins.html
   in ABT-1 on 2026-08-14; the count was true when written and went stale
   with the section, unnoticed for a day. The reasoning is untouched and the
   trap is unchanged: this file is still linked by ZERO of the pages whose
   comments name it, which is exactly why a grep is not a consumer count.

   THE COMPOUND IS THE ONLY BARRIER NOW, and that is why it must not be
   loosened. Living in home.css gave this group a second, accidental
   barrier — that sheet was linked by one page. This file is not, so the
   selector carries the whole job alone:
   (2) `.band-slate.quote-feature` REQUIRES the quote-feature class, which
       is set in partials/components/quote-block.html. R1 re-keyed this
       group; it read
       `#problem ~ .band-slate` until then. THE COMPOUND IS NOT
       DECORATION: the class alone is (0,1,0), which merely TIES
       quote-block.css's `.band-slate` and would win on load order only,
       so re-ordering the <link> tags in the <head> could undo it.
       Compounded it is (0,2,0) against (0,1,0) — (0,2,1) for the
       ::before pair, (0,3,0) for .portrait — and wins on SPECIFICITY,
       which is the barrier the old selector's (1,1,0) provided and the
       property this group must not lose.

       WHY THE OLD SELECTOR WENT. It keyed five rules to a sibling section
       existing earlier in the document. When R1 moved #problem to another
       page they unkeyed together, and it was silent: MEASURED, nine
       computed properties changed and the band fell back to
       quote-block.css's defaults — a different texture file, and white
       dropping to cream on --slate, on a band already carrying a contrast
       foreclosure. A neighbour is not a barrier; neighbours move.

   EVERY LONGHAND IS RESTATED, NOT PARTIALLY OVERRIDDEN. quote-block.css
   sets mask-size:cover, mask-repeat:no-repeat and mask-position:center;
   overriding size alone leaves the other two underneath and paints a
   single 18px tile. Measured when simulated: 0.04% of this band inked,
   against 49.90% with all three moving together.

   OPACITY .14 -> .10 IS THE ONLY CONTRAST MOVE IN THIS UNIT, and it is
   safe for a reason worth writing down. On this band the ink is DARKER
   than the ground (--ink onto --slate), so inking RAISES every ratio and
   the binding case is the BARE ground — which this edit does not touch.
   Lowering the opacity only pulls the inked ground back toward bare:
   87,105,120 -> 89,108,124. Binding figures, unchanged either way:
   blockquote 4.3009 (40px/700, LARGE, threshold 3.0) and cite.attrib
   4.8755 (10.5px/400, small, threshold 4.5 — margin 0.3755). Note that
   4.3009 is --on-slate against 4.5 and would FAIL as small text; this
   band is legal because its type is LARGE, not because the colour is
   safe. Re-measure both if --ink, --slate or either font-size moves.
   --------------------------------------------------------------------- */
.band-slate.quote-feature::before{
  -webkit-mask-image:url(../../assets/longitude-texture.svg);
          mask-image:url(../../assets/longitude-texture.svg);
  -webkit-mask-size:18px 18px;     mask-size:18px 18px;
  -webkit-mask-repeat:repeat;      mask-repeat:repeat;
  -webkit-mask-position:top left;  mask-position:top left;
  opacity:.10;
}

/* ---------------- 8q · QUOTE BAND CONTENT MATCH ----------------------

   Director ruling (8q, retained under its original number — the record is
   not renumbered): this band matches the utilities quote band in EVERY
   respect EXCEPT background colour. The grounds stay different — violet on
   utilities, slate here. SCOPING IS BY SELECTOR: everything below is keyed
   to `.band-slate.quote-feature`, so it reaches the band wherever the
   partial that sets that class is included, and reaches nothing else. The
   original scoping used `#problem ~` and then home.css's single-page
   linkage; both are gone.

   MEASURED DIFFERENCES, from the rendered DOM of both bands. Only three
   were independent; the .rv width and every height delta are CONSEQUENCES
   of the portrait width (.rv absorbs 1040 - portrait - 57.6 gap) and need
   no rule of their own.

   (1) COLOUR --paper -> --white, set on the band and inherited by .in,
   .rv and blockquote exactly as it is on utilities. cite.attrib already
   computed --white on both bands and is untouched. THIS IMPROVES
   CONTRAST: blockquote 4.3009 -> 4.8755 against the bare slate. 4.8755
   clears the 4.5 SMALL-text threshold, so after this unit the band no
   longer depends on its LARGE (40px/700) classification to be legal.
   That dependency was a standing site-wide fragility and this retires it
   for this band. Re-measure if --white, --slate or the ink opacity move.

   (2) GOLD MARK 96px -> 160px. line-height is restated because the two
   bands do not share a RATIO — 43.2/96 = .45 here, 67.2/160 = .42 on
   utilities — so setting font-size alone would land 72px, not 67.2px.
   margin-bottom 18px -> 10px.

   (3) PORTRAIT 220px -> 300px, square, all three of flex-basis, width and
   height moved together. A max-width against a pinned height is the
   documented distortion pattern on this project and is not used here.

   THERE IS NO 250px CAP IN THE CASCADE. The "250" on this project is a
   COMMENT at components/quote-block.css:81, not a declaration — a warning
   threshold reading "IF A LATER REVISION WANTS MORE THAN 250, STOP: at
   DPR 2 that exceeds the native file." Nothing clamps 300. That warning
   is CORRECT and this unit crosses it KNOWINGLY on director approval:

       McCanna  500x500 native, 300 CSS px  ->  0.600x DPR 1, 1.200x DPR 2
       EXCEEDS SOURCE AT DPR 2. Approved as a temporary display state; a
       higher-resolution original from the client is pending.

   THE FILE IS NOT TOUCHED. Approval to USE at a display size is NOT
   permission to PROCESS. lh-portrait-mccanna.jpg is never generated,
   upscaled, enhanced, resampled, sharpened, denoised, interpolated or
   re-encoded, and never used as style, reference, frame-capture or
   synthesis input. It is hashed, never opened. This is Pete McCanna.

   THE BREAKPOINT IS RESTATED IN FULL because a media query adds no
   specificity. quote-block.css drops .portrait to 170px at max-width:760;
   utilities restates 220px there. This band matches utilities, so it
   takes 220px too — without the restatement the 300px above would win at
   every width and the narrow layout would break.
   --------------------------------------------------------------------- */
.band-slate.quote-feature{color:var(--white)}
.band-slate.quote-feature blockquote::before{
  font-size:160px;
  line-height:67.2px;
  margin-bottom:10px;
}
.band-slate.quote-feature .portrait{
  flex:0 0 300px;
  width:300px;
  height:300px;
}
@media(max-width:760px){
  .band-slate.quote-feature .portrait{
    flex:0 0 220px;
    width:220px;
    height:220px;
  }
}
