/* Patent Case Watch — styles */

:root {
  --paper: #fafaf7;
  --card: #ffffff;
  --ink: #1c2128;
  --ink-soft: #4b5563;
  /* Darkened from #8a919c, which was 3.18:1 on --card, 3.04:1 on --paper and
   * 2.78:1 on --neutral-bg, against the 4.5:1 that WCAG 1.4.3 requires for text
   * this size. It carried eleven components -- metadata rows, filter labels,
   * the result count, breadcrumbs, definition-list terms, the footer -- so one
   * token accounted for eleven of the thirteen contrast failures on the site.
   * Hue and saturation are unchanged (216.7deg -> 217.9deg); only lightness
   * moved, so the grey still reads as the same cool grey. Now 5.29 / 5.06 /
   * 4.63:1 on those three backgrounds, and still clearly lighter than
   * --ink-soft, which is what keeps the three-step text hierarchy legible. */
  --ink-faint: #656c78;
  --line: #e5e4df;
  --navy: #1f3a5f;
  --navy-dark: #16293f;
  --edtx: #8c2f39;      /* deep brick — E.D. Tex. */
  --cafc: #1f3a5f;      /* federal navy — Fed. Cir. */
  /* Only --cafc-bg survives, on the tag ribbons. --edtx-bg went with the venue
   * badge's fill; a token nothing references is a colour the next person has to
   * decide whether to trust. */
  --cafc-bg: #eaeff5;
  --granted: #1e6f43;
  --granted-bg: #e7f2ea;
  --denied: #9b2c2c;
  --denied-bg: #f9ebeb;
  /* Darkened from #8a6d1a (4.34:1 on --mixed-bg) -- a marginal 1.4.3 miss, and
   * the only holding chip that failed. Taken to 5.05:1 rather than the bare
   * 4.5:1 so the four chips sit in the same band: granted 5.36, denied 6.49,
   * neutral 6.62. Hue is untouched (44.5deg -> 44.4deg), so it is still the
   * same ochre. */
  --mixed: #7e6318;
  --mixed-bg: #f7f1de;
  --neutral: #4b5563;
  --neutral-bg: #eef0f2;
  --serif: "Iowan Old Style", "Palatino", "Palatino Linotype", Georgia, "Times New Roman", serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;

  /* A 4px spacing scale. The values it replaces -- 14, 18, 20, 22, 24, 26, 28,
   * 30, 34 -- are each within 2px of a multiple of four and none of them agree
   * with any other, which is what made the page feel slightly loose without any
   * single thing looking wrong. */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 20px;
  --s6: 24px;
  --s7: 32px;

  /* Three type sizes in a feed card, not five. It was saying four things --
   * where, when, what, and a two-line excerpt -- in 10.5, 11.5, 12.5, 14 and
   * 15.5px, which reads as five levels of importance for four pieces of
   * information. Label / meta / body, and the serif title above them. */
  --fs-label: 11px;
  --fs-meta: 13px;
  --fs-body: 15px;
  --fs-title: 19px;

  /* One radius. Cards were 8px, chips and badges 4px, buttons 6px and 999px. */
  --radius: 6px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--sans);
  background: var(--paper);
  color: var(--ink);
  line-height: 1.55;
  font-size: 15.5px;
}

a { color: var(--navy); text-decoration: none; }
a:hover { text-decoration: underline; }

/* WCAG 1.4.1 Use of Colour (Level A). A link inside running prose was
 * distinguished from the text around it by colour alone: navy #1f3a5f against
 * ink #1c2128 is 1.41:1, where the criterion wants 3:1. Underlining is the
 * only fix that does not require repainting either colour.
 *
 * Scoped to `p a` because that is exactly what axe-core's link-in-text-block
 * rule flagged, on every page it flagged anything: the contact link, the
 * corrections address, and the govinfo link in the footer sentence. Links that
 * already carry a non-colour affordance are deliberately untouched -- the
 * masthead wordmark, card titles, tag ribbons, the PDF button, and the year-nav
 * chips are all shapes or headings, not words in a sentence.
 *
 * Breadcrumbs are included for a different reason: every crumb is --ink-faint,
 * so the linked crumbs and the current-page crumb were the same colour as each
 * other. There was no visual cue at all, which is worse than a weak one. */
p a,
.breadcrumb a { text-decoration: underline; text-underline-offset: 2px; }

.wrap { max-width: 880px; margin: 0 auto; padding: 0 24px; }

/* ---------- Masthead ---------- */

.masthead {
  background: var(--navy-dark);
  color: #fff;
  border-bottom: 3px solid var(--edtx);
}

/* The digest link sits in the top-right of the masthead, in space the header
   already occupies — so the second route to the signup costs no height at all.
   Grid, not float: the tagline must be free to wrap to as many lines as it
   needs without the link moving or overlapping it. */
.masthead .wrap {
  padding-top: 34px;
  padding-bottom: 30px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  column-gap: var(--s4, 16px);
}
.masthead .brand,
.masthead .tagline { grid-column: 1; }
.masthead-digest {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  justify-self: end;
  padding: 6px 12px;
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: var(--radius, 6px);
  font-size: var(--fs-meta, 13px);
  font-weight: 500;
  line-height: 1.2;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
}
.masthead-digest:hover,
.masthead-digest:focus-visible {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.75);
}
/* It stays in the top-right corner at every width, including phones. Letting it
   drop under the wordmark on narrow screens added 42px to the masthead —
   measured — which is the opposite of the point: on a 375x812 phone that height
   comes straight out of the opinion list. The tagline wraps in column one
   instead, which costs nothing. */
@media (max-width: 560px) {
  .masthead-digest { padding: 5px 10px; }
  .masthead-digest-long { display: none; }
}

.brand {
  font-family: var(--serif);
  font-size: 30px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #fff;
  /* index.html carries the wordmark as <h1>; every other page uses a <div> and
   * spends its <h1> on the page title. Zeroing the margin means the element can
   * be either without the browser's default h1 margin moving the masthead. */
  margin: 0;
}
.brand a { color: #fff; }
.brand a:hover { text-decoration: none; }

.brand .amp { color: #c8a24a; font-style: italic; }

.tagline {
  margin-top: 6px;
  font-size: 13.5px;
  color: #b9c4d2;
  letter-spacing: 0.02em;
}

/* ---------- Filter bar ---------- */

/* Hidden by default, because every control inside is built by JavaScript: a
 * no-JS visitor would otherwise get four labels, two empty dropdowns and a
 * search box that accepts typing and does nothing.
 *
 * The reveal has to happen in CSS, not JavaScript. It was previously the
 * hidden attribute cleared in app.js init(), which meant the browser painted
 * the page with no filter bar and then app.js inserted ~300px above the feed:
 * Lighthouse measured CLS 0.292 on mobile, and an A/B against an
 * otherwise-identical page put 0.217 of that on this one element. (The
 * commit that introduced it claimed CLS 0 in good faith -- the headless
 * browser it was measured in never painted, so it recorded no shifts at all.
 * CLS cannot be measured anywhere that does not paint.)
 *
 * `scripting: enabled` is evaluated while the stylesheet is parsed, before
 * anything is painted, so the bar is present and correctly sized in the first
 * frame. No inline <style> (blocked by style-src 'self'), no head script, no
 * extra request. app.js keeps a display fallback for browsers that do not
 * support the media feature -- those still get a working filter bar, just
 * with the shift this removes everywhere else. */
.filters {
  display: none;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 2px rgba(20, 25, 35, 0.04);
}

@media (scripting: enabled) {
  .filters { display: block; }
}

/* A grid, because a wrapping flex row cannot be relied on not to wrap.
 *
 * The controls are a fixed set of five groups plus the result count. As a flex
 * row they re-flowed whenever any child's width changed, and one did: .pills
 * shipped 42px and app.js took it to 198px, pushing the fifth group onto a
 * second line and dropping the feed 28px at 1440 — CLS 0.2652, measured on
 * production, five times the mobile figure Lighthouse reported at launch
 * because Lighthouse runs mobile-emulated by default.
 *
 * The columns are `auto` and `1fr`, with no pixel reservations anywhere: the
 * venue buttons now ship in the HTML (see the vp: markers in index.html), so
 * the first column is sized by real content in the first frame. A grid with a
 * declared column count cannot re-flow no matter what any child does, which is
 * the property that matters — the previous fix reserved the bar's height and
 * was undone by its width. */
.filters .wrap {
  padding-top: 12px;
  padding-bottom: 12px;
  display: grid;
  /* Venue is `auto` because its buttons are real content in the first frame.
   * The other four are `1fr` and NOT `auto`: an auto track is sized by its
   * contents, and a <select> that ships with no options has a small max-content
   * width that grows the moment app.js adds them — which re-flowed the row all
   * over again, 0.027 of it, after the pills were fixed. A fraction track is
   * sized by the container, so nothing a control does to itself can move its
   * neighbours.
   *
   * The four fractions are not equal. Search carries a 22-character
   * placeholder and is the control people actually reach for, so it gets the
   * widest track; Year holds "All years" and a four-digit number and needs the
   * least. Sizing them by content instead would reintroduce exactly the
   * dependency this grid exists to remove. */
  grid-template-columns: auto minmax(0, 1fr) minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 0.8fr);
  gap: 12px 24px;
  align-items: end;
}

.filter-group { min-width: 0; }

/* Sixth child of a five-column grid, so it lands on a row of its own. Told to
   span every column, or it inherits the width of the venue column and the count
   wraps inside 198px. */
/* Sixth child of a five-column grid, so it lands on a row of its own. Told to
 * span every column, or it inherits the width of the venue column and the count
 * wraps inside 198px.
 *
 * All of its layout is here. It used to be declared twice — once beside the
 * feed styles with `margin-left: auto`, which was how it pushed itself right in
 * the old flex bar and does nothing at all in a grid, and once more in the
 * 640px block. Two rules for one element, one of them dead, is how the next
 * person changes the wrong one. */
.filter-meta {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--s1);
}

/* Fill the grid track. A fixed 190px width sat the controls flush against each
   other and the column gap never appeared. */
.filters .wrap .filter-select,
.filters .wrap .year-select,
.filters .wrap .search-input { width: 100%; max-width: none; }

/* Two rows on a phone, instead of four that became six.
 *
 * Measured at 320px: the bar was 375px tall in a 568px viewport, with the feed
 * starting at 548px — an iPhone SE user saw five filter controls and no
 * opinions at all. It is 170px now.
 *
 * Most of that comes from the labels, which move into the accessibility tree
 * rather than off the page: every control already carries its own aria-label,
 * and each <select>'s first option reads "All subjects" / "All years" /
 * "All judges", so a sighted user loses a heading that was repeating what the
 * control below it already said, at 17px of vertical each. The venue group
 * keeps its name through aria-labelledby, which is why the label element stays
 * in the DOM instead of being deleted. */
@media (max-width: 760px) {
  .filters .wrap {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 8px;
  }
  .filter-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .filter-group:first-child { grid-column: 1 / -1; }
}

.filter-label {
  font-size: var(--fs-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-faint);
  margin-bottom: var(--s1);
}

/* No min-height any more. It was reserving one row for a container that shipped
 * empty and was filled by app.js; the buttons are now in the HTML, so the row
 * is reserved by being there. Reserving the height was also only ever half the
 * problem — the width was what re-flowed the bar. */
.pills { display: flex; flex-wrap: wrap; gap: 6px; }

.pill {
  font-family: var(--sans);
  font-size: 12.5px;
  padding: 4px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink-soft);
  cursor: pointer;
  transition: all 0.12s ease;
  white-space: nowrap;
}

.pill:hover { border-color: var(--navy); color: var(--navy); }

.pill.active {
  background: var(--navy);
  border-color: var(--navy);
  color: #fff;
}

.search-input {
  font-family: var(--sans);
  font-size: 13px;
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  width: 190px;
  outline: none;
  transition: border-color 0.12s ease;
}
.search-input:focus { border-color: var(--navy); }
.search-input::placeholder { color: var(--ink-faint); }

.year-select,
.filter-select {
  font-family: var(--sans);
  font-size: 12.5px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink-soft);
  outline: none;
}
.year-select:focus,
.filter-select:focus { border-color: var(--navy); }

.filter-select {
  width: 190px;
  max-width: 190px;
}

.load-more {
  display: block;
  width: 100%;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  color: var(--navy);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px;
  cursor: pointer;
  margin: 4px 0 24px;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.load-more:hover { border-color: var(--navy); box-shadow: 0 2px 8px rgba(20,25,35,0.06); }

.meta-note { color: var(--ink-faint); font-size: 12px; }

.auto-note {
  font-size: 12.5px;
  color: var(--ink-faint);
  background: var(--neutral-bg);
  border-left: 3px solid var(--line);
  padding: 8px 12px;
  border-radius: 0 4px 4px 0;
  margin-bottom: 14px;
}

/* No white-space: nowrap here. "Showing 100 of 4,580 opinions - newest decision
 * first" cannot fit on one line at 320px, and forcing it produced 18px of
 * horizontal page overflow -- a WCAG 1.4.10 Reflow failure, which also covers
 * 400% zoom on a 1280px screen. Removing nowrap takes the overflow to exactly
 * 0; the count simply wraps to a second line on narrow screens. */
.result-count { font-size: 12.5px; color: var(--ink-faint); padding-top: 2px; }

.clear-btn {
  font-size: 12.5px;
  color: var(--edtx);
  background: none;
  border: none;
  cursor: pointer;
  /* Vertical padding to clear the 24px minimum tap target (WCAG 2.5.8). This
     is the only standalone control on the page that missed it, and on a phone
     it sits right under the result count where a near-miss hits nothing. The
     padding only counts because the visible state below is inline-BLOCK; on a
     plain inline box, vertical padding does not affect the line box at all. */
  padding: 5px 0;
  text-decoration: underline;
  display: none;
}
.clear-btn.visible { display: inline-block; }

/* ---------- Feed ---------- */

.feed .wrap { padding-top: 28px; padding-bottom: 60px; }

.feed-item {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s4) var(--s5) var(--s3);
  margin-bottom: var(--s3);
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.feed-item:hover {
  border-color: #c9ccd2;
  box-shadow: 0 3px 12px rgba(20, 25, 35, 0.07);
}

.item-meta {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-bottom: var(--s1);
  flex-wrap: wrap;
}

/* A kicker, not a badge.
 *
 * The venue was a filled pill sitting next to a filled holding chip, and the
 * two read as equally weighted. They are not: the venue is a standing category
 * that only ever takes two values, while the holding is the outcome — the one
 * thing on a card a litigator is actually scanning for, and the only place on
 * this site where colour carries meaning rather than decoration. Two coloured
 * fills per card meant the meaningful one had to compete with a label.
 *
 * Dropping the fill raises contrast rather than lowering it, since the tint was
 * lighter than the page: E.D. Tex. goes 7.04:1 to 8.14:1, Fed. Cir. 9.93:1 to
 * 11.48:1, both well past the 4.5:1 this size needs. The extra letter-spacing
 * is doing the job the fill used to — holding the word together as a unit
 * without drawing a box around it. */
.venue-badge {
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  background: none;
  padding: 0;
  border-radius: 0;
}
.venue-badge.edtx { color: var(--edtx); }
.venue-badge.cafc { color: var(--cafc); }

.item-date, .item-type { font-size: var(--fs-meta); color: var(--ink-faint); }
/* .item-meta is a wrapping flex row, so this separator belongs to the item that
   follows it and travels with it onto the next line — leaving an orphaned "·"
   at the start of that line. It only ever wraps on narrow screens, where the
   line break already separates the two values, so the dot is dropped there
   rather than dragged along. */
.item-type::before { content: "·"; margin-right: 10px; }
@media (max-width: 640px) {
  .item-type::before { content: none; }
}

.item-title {
  font-family: var(--serif);
  font-size: var(--fs-title);
  font-weight: 600;
  line-height: 1.35;
  margin-bottom: var(--s1);
}
.item-title a { color: var(--ink); }
.item-title a:hover { color: var(--navy); }

.item-teaser {
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--ink-soft);
  margin-bottom: var(--s3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* Teasers are not in feed-index.js; they arrive later from the per-year
     snippet shard. Reserve the two clamped lines up front so the cards do not
     render short and then push everything below them down when the shard
     lands — measured at a 4,340 px jump for a 100-card page. Must equal
     2 x the line-height declared directly above: this block sets its own
     rather than inheriting the body's 1.55, so the two have to be changed
     together or the reservation stops matching the clamp. */
  min-height: 3.2em;
}

/* Shown in place of a card excerpt when that year's snippet shard could not
 * be loaded. Must come after .item-teaser: both are single-class selectors,
 * so specificity ties and source order is what decides the colour.
 * Deliberately distinguished from real excerpt text -- an excerpt is the
 * court's own words, and a failure notice in the same voice would be
 * indistinguishable from one. Keeps the reserved two-line height, so a
 * failed shard still causes no layout shift. */
.item-teaser-unavailable { color: var(--ink-faint); font-style: italic; }

.item-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) 6px;
  align-items: center;
}

.tag {
  font-size: 12px;
  color: var(--ink-soft);
  background: var(--neutral-bg);
  padding: 3px 10px;
  border-radius: 3px;
}

.holding-chip {
  font-size: 12px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 3px;
  margin-left: auto;
  white-space: nowrap;
}
.holding-granted   { color: var(--granted); background: var(--granted-bg); }
.holding-denied    { color: var(--denied);  background: var(--denied-bg); }
.holding-mixed     { color: var(--mixed);   background: var(--mixed-bg); }
.holding-neutral   { color: var(--neutral); background: var(--neutral-bg); }

.empty-state {
  text-align: center;
  color: var(--ink-faint);
  padding: 60px 0;
  font-size: 15px;
}

/* ---------- Detail page ---------- */

.breadcrumb {
  padding-top: 26px;
  font-size: 13px;
  color: var(--ink-faint);
}
.breadcrumb a { color: var(--ink-faint); }
.breadcrumb a:hover { color: var(--navy); }

/* The trail is an <ol> (see breadcrumb_html() in pipeline/gen_data_full.py), so
 * the separator is generated content rather than a literal character in the
 * markup: decoration that is not in the accessibility tree and cannot be read
 * out as "single right-pointing angle quotation mark". Laid out as a wrapping
 * flex row so a long case caption breaks onto a second line instead of forcing
 * the page sideways — the crumb carries the full caption, which is regularly
 * wider than a phone. */
.breadcrumb ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
}
/* 4px each side reproduces the two literal spaces the old markup had around the
 * character at this font size; the colour is inherited rather than set, so the
 * trail looks byte-for-byte the same as before the semantics changed. */
.breadcrumb li + li::before {
  content: "\203A";
  margin: 0 4px;
}

.opinion-head { padding-top: var(--s4); padding-bottom: var(--s1); }

.opinion-title {
  font-family: var(--serif);
  font-size: 27px;
  font-weight: 600;
  line-height: 1.3;
  /* More space above than the 10px this had. The venue above it is no longer a
     filled badge with its own padding holding it apart from the title — it is
     bare letter-spaced text, and at 10px it read as part of the heading rather
     than as a kicker over it. */
  margin: var(--s3) 0 var(--s5);
}

.opinion-meta {
  display: grid;
  grid-template-columns: 124px 1fr;
  gap: var(--s1) var(--s4);
  font-size: var(--fs-meta);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s4) var(--s5);
  margin-bottom: var(--s5);
}
.opinion-meta dt {
  font-weight: 700;
  color: var(--ink-faint);
  text-transform: uppercase;
  font-size: var(--fs-label);
  letter-spacing: 0.08em;
  padding-top: 1px;
}
.opinion-meta dd { color: var(--ink); }

.ribbons { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 22px; }

.ribbon {
  font-size: 12px;
  font-weight: 600;
  color: var(--navy);
  background: var(--cafc-bg);
  border-left: 3px solid var(--navy);
  padding: 5px 12px;
  border-radius: 0 4px 4px 0;
}

.holding-banner {
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  font-size: 14px;
  padding: 10px 16px;
  border-radius: 6px;
  margin-bottom: 26px;
}
.holding-banner .label { font-weight: 800; text-transform: uppercase; font-size: 12px; letter-spacing: 0.05em; }

.pdf-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: var(--navy);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  padding: 10px 18px;
  border-radius: 6px;
  margin-bottom: 8px;
  transition: background 0.12s ease;
}
.pdf-btn:hover { background: var(--navy-dark); text-decoration: none; }
.pdf-btn svg { flex-shrink: 0; }

/* "Cite this opinion": selectable plain text, no copy button. Set as a quiet
 * card rather than a heading-and-paragraph, so a reader scanning for it finds
 * it at a glance, and a reader who is not looking for it is not detained.
 *
 * The citation line is serif and slightly larger than its surroundings because
 * it is the thing being offered; everything else in the block explains it.
 * user-select is left alone -- select-all-on-click sounds helpful and prevents
 * the common case, which is dragging out the case name without the docket. */
.cite-this {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s4) var(--s5);
  margin: 0 0 var(--s6);
  max-width: 68ch;
}
.cite-this h2 {
  font-family: var(--sans);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 var(--s2);
}
.cite-line {
  font-family: var(--serif);
  font-size: var(--fs-body);
  line-height: 1.5;
  margin: 0 0 var(--s3);
}
.cite-meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--s1) var(--s3);
  margin: 0 0 var(--s3);
  font-size: var(--fs-meta);
}
.cite-meta dt { color: var(--ink-faint); }
/* The permalink and the govinfo ID are long, unbreakable-looking strings in a
 * narrow column; without this they overflow the card on a phone. */
.cite-meta dd { margin: 0; overflow-wrap: anywhere; }
.cite-meta code { font-size: 12px; }
.cite-note { font-size: 12px; line-height: 1.5; color: var(--ink-faint); margin: 0; }
@media (max-width: 30rem) {
  /* Below ~480px a max-content label column plus a permalink leaves the value
     column a few characters wide. Stack instead. */
  .cite-meta { grid-template-columns: 1fr; gap: 0; }
  .cite-meta dt { margin-top: var(--s2); }
}

.source-link { font-size: 12.5px; color: var(--ink-faint); margin-bottom: 30px; }

.summary-section h2 {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 600;
  border-bottom: 1px solid var(--line);
  padding-bottom: 8px;
  margin-bottom: 14px;
}

.summary-section p {
  font-size: 15px;
  color: #2a3038;
  margin-bottom: 14px;
  line-height: 1.7;
}

.opinion-body { padding-bottom: 70px; }

/* Single column, at every width.
 *
 * There was a 300px second column here, holding an ad unit, and the page was
 * widened to 1040px so it would not squeeze the text. With ads gone the column
 * was empty and the text sat at 700px inside a 1040px page, off-centre, with
 * nothing beside it.
 *
 * Giving the column a job instead was tried and measured: put the document's
 * own metadata in it and the values do not fit. "Docket Entry" is a
 * docket-sheet sentence up to ~120 characters, and 300px less a 116px label
 * gutter leaves about 170px, so Court broke over five lines, Case No. over
 * four, Docket Entry over ten. The stickiness argument does not survive
 * measurement either: detail pages render 1,245-2,090px tall at 1440x900, so
 * there is between 0.4 and 1.3 of a viewport for a sticky element to accompany.
 *
 * 760px is the measure, not 1040px: the same body size as the feed at roughly
 * 78 characters a line.
 *
 * The .opinion-layout and .opinion-main wrappers that made the grid are gone
 * from the markup too — with the rail removed they were two nested divs set to
 * display:block around the whole page body, on 6,692 pages. */
@media (min-width: 940px) {
  .wrap.opinion-body { max-width: 760px; }
}

.actions { margin: 26px 0 6px; }

/* The download button at the head of the Full Text panel. It leads the panel,
 * so its top margin is the panel's own padding and only the space beneath it
 * is this rule's business. */
.actions.actions-top { margin: 0 0 20px; }

/* The extraction disclaimer, now at the foot of the page under the citation
 * block. Set apart from .cite-this above it with a rule, so it reads as a note
 * about the whole page rather than as one more line of the citation. */
.text-provenance {
  margin: 18px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-faint);
  max-width: 68ch;
}

/* ---------- Static archive & opinion pages ---------- */

.archive-body { padding-bottom: 70px; }

.archive-title {
  font-family: var(--serif);
  font-size: 26px;
  font-weight: 600;
  margin: 18px 0 6px;
}

.archive-intro { font-size: 14px; color: var(--ink-soft); margin-bottom: 22px; }

.archive-month {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 600;
  border-bottom: 1px solid var(--line);
  padding-bottom: 6px;
  margin: 26px 0 10px;
}

.archive-list { list-style: none; }
.archive-list li {
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
  display: flex;
  gap: 10px;
  align-items: baseline;
  flex-wrap: wrap;
}
.archive-list .item-date { flex-shrink: 0; min-width: 88px; }

.year-nav { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 30px; }
.year-nav a {
  font-size: 13.5px;
  padding: 5px 13px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  color: var(--navy);
}
.year-nav a:hover { border-color: var(--navy); text-decoration: none; }

.related-docs { margin-top: 34px; }
.related-docs h2 {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 600;
  border-bottom: 1px solid var(--line);
  padding-bottom: 8px;
  margin-bottom: 10px;
}
.related-docs ul { list-style: none; }
.related-docs li { padding: 6px 0; font-size: 14px; display: flex; gap: 10px; flex-wrap: wrap; align-items: baseline; }
.related-docs .item-date { flex-shrink: 0; min-width: 88px; }

.footer-nav { margin-bottom: 12px; font-size: 13.5px; }

/* ---------- Accessibility ---------- */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--navy-dark);
  color: #fff;
  padding: 10px 18px;
  border-radius: 0 0 6px 0;
  font-size: 14px;
}
.skip-link:focus {
  left: 0;
  text-decoration: none;
  outline: 2px solid #c8a24a;
}

.pill:focus-visible,
.filter-select:focus-visible,
.year-select:focus-visible,
.search-input:focus-visible,
.load-more:focus-visible,
.clear-btn:focus-visible,
a:focus-visible {
  outline: 2px solid var(--navy);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

.unavailable-note {
  font-size: 14px;
  color: var(--ink);
  background: var(--denied-bg);
  border-left: 3px solid var(--denied);
  padding: 12px 16px;
  border-radius: 0 6px 6px 0;
  margin: 26px 0 8px;
  max-width: 640px;
}

a.ribbon:hover { text-decoration: none; border-left-color: var(--edtx); }

.noscript-note {
  font-size: 14px;
  color: var(--ink-soft);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 16px;
}

/* ---------- Footer ---------- */

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--card);
}
.site-footer .wrap {
  padding-top: 22px;
  padding-bottom: 30px;
  font-size: 12.5px;
  color: var(--ink-faint);
  line-height: 1.7;
}

/* ---------- Responsive ---------- */

@media (max-width: 640px) {
  /* The 760px block above already sets this bar's gap and columns; this only
     changes where the count sits, which reads better ranged left once the
     controls are a two-column stack rather than a row. */
  .filter-meta { align-items: flex-start; }
  .holding-chip { margin-left: 0; }
  .opinion-meta { grid-template-columns: 1fr; gap: 2px; }
  .opinion-meta dt { margin-top: 10px; }
  .opinion-title { font-size: 22px; }
  .brand { font-size: 24px; }
}

/* ---------- Digest signup (Phase 1) ---------- */
/* Rendered only when apiOrigin is configured; see subscribe_form_html() in
   pipeline/gen_data_full.py. Two placements share these styles: a full-width
   band under the masthead on the feed, and a quieter block at the foot of each
   opinion page. */

.subscribe {
  background: var(--card);
  border-bottom: 1px solid var(--line);
  padding: 22px 0 24px;
}

/* The feed banner is one row: heading, field, button. It was a stacked block —
   heading, a three-line paragraph, then the form — measuring 275px on a
   1280x800 desktop and 421px on a 375x812 phone, which pushed the opinion list
   so far down that one card was visible on desktop and none on mobile.
   The .wrap here becomes the row, so the heading sits beside the field rather
   than above it, and the notes span the full width underneath. */
.subscribe-feed { padding: 12px 0 14px; }
.subscribe-feed .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2, 8px) var(--s4, 16px);
}
.subscribe-feed .subscribe-h {
  margin: 0;
  font-size: 1.02rem;
  white-space: nowrap;
}
/* The form is already a flex row; let it take the remaining width and stop the
   notes from forcing the field onto its own line at desktop widths. */
.subscribe-feed .subscribe-form {
  flex: 1 1 clamp(260px, 40%, 460px);
  align-items: center;
}
.subscribe-feed .subscribe-input { flex: 1 1 180px; }
.subscribe-feed .subscribe-note,
.subscribe-feed .subscribe-premium { flex-basis: 100%; }
.subscribe-feed .subscribe-premium {
  margin: 0;
  padding-top: 0;
  border-top: 0;
}
/* Below the fold-critical width the row stacks, which is correct — a 320px
   phone cannot hold a heading, a field and a button side by side. It is still
   far shorter than the block version because the paragraph is gone. */
@media (max-width: 620px) {
  .subscribe-feed .wrap { gap: var(--s2, 8px); }
  .subscribe-feed .subscribe-h { flex-basis: 100%; }
}
.subscribe-doc {
  border: 1px solid var(--line);
  border-left: 3px solid var(--navy);
  background: var(--paper);
  margin: 32px 0 8px;
  padding: 18px 0 20px;
}
.subscribe-doc .wrap { padding: 0 20px; max-width: none; }

.subscribe-h {
  font-family: var(--serif);
  font-size: 1.22rem;
  margin: 0 0 4px;
  color: var(--ink);
}
.subscribe-doc .subscribe-h { font-size: 1.08rem; }

.subscribe-blurb {
  margin: 0 0 12px;
  font-size: 0.9rem;
  color: var(--ink-soft);
  max-width: 62ch;
}

.subscribe-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
/* The visible label is redundant next to the placeholder for sighted users but
   is what screen readers announce; keep it in the accessibility tree only. */
.subscribe-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
.subscribe-input {
  flex: 1 1 240px;
  min-width: 0;
  font-family: var(--sans);
  font-size: 0.95rem;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: #fff;
  color: var(--ink);
}
.subscribe-input:focus {
  outline: 2px solid var(--navy);
  outline-offset: 1px;
  border-color: var(--navy);
}
.subscribe-input:disabled { background: var(--neutral-bg); color: var(--ink-faint); }

.subscribe-btn {
  flex: 0 0 auto;
  font-family: var(--sans);
  font-size: 0.9rem;
  font-weight: 600;
  padding: 10px 18px;
  border: 1px solid var(--navy-dark);
  border-radius: 3px;
  background: var(--navy);
  color: #fff;
  cursor: pointer;
}
.subscribe-btn:hover:not(:disabled) { background: var(--navy-dark); }
.subscribe-btn:disabled { opacity: 0.55; cursor: default; }

/* The honeypot must be unreachable for people — including keyboard and screen
   reader users — while staying a real, fillable field for a scripted client.
   Hence off-canvas rather than display:none, which some bots detect. */
.subscribe-hp {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.subscribe-note {
  flex: 1 1 100%;
  margin: 2px 0 0;
  font-size: 0.76rem;
  color: var(--ink-faint);
}
.subscribe-status {
  flex: 1 1 100%;
  margin: 0;
  font-size: 0.85rem;
}
.subscribe-status:empty { display: none; }

/* The detail-page stand-in for the signup form. Opinion pages carry zero
   executable JavaScript, so they get a link to the feed's form rather than the
   form itself — this is a paragraph with an anchor in it, nothing more. */
.subscribe-cta {
  margin-top: var(--s6, 32px);
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
}
.subscribe-cta p {
  margin: 0;
  font-size: var(--fs-meta);
  color: var(--ink-faint);
}

/* The paid-tier notice. Sits below the form and outside it, separated by a rule
   rather than a box: it is an aside about something that does not exist yet,
   and giving it a card would make it compete with the control that actually
   does something. */
.subscribe-premium {
  margin: var(--s4) 0 0;
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
  font-size: var(--fs-meta);
  color: var(--ink-faint);
}
.subscribe-status.is-ok { color: var(--granted); font-weight: 600; }
.subscribe-status.is-error { color: var(--denied); font-weight: 600; }

@media (max-width: 560px) {
  .subscribe-form { gap: 8px; }
  .subscribe-input, .subscribe-btn { flex: 1 1 100%; }
}

/* --- Full Text / Analysis tabs -------------------------------------------
 *
 * CSS-only. Detail pages carry zero executable JavaScript, so the two radios
 * below ARE the tab state: :checked drives which panel shows and which label
 * reads as selected. A native radio group also brings arrow-key navigation for
 * free, which is the keyboard behaviour ARIA tabs specify and which scripted
 * markup would have to reimplement.
 *
 * The radios are moved off-screen rather than display:none — a hidden input is
 * not focusable, which would make the tabs keyboard-unreachable. */
.tab-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.tab-strip {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 20px;
  /* Sticks while reading a 40-page opinion, so switching view never means
   * scrolling back to the top. position:sticky needs no script. */
  position: sticky;
  top: 0;
  background: var(--paper);
  z-index: 2;
}

.tab {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-faint);
  padding: 10px 16px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  user-select: none;
}

.tab:hover { color: var(--ink); }

#tab-text:checked     ~ .tab-strip .tab[for="tab-text"],
#tab-analysis:checked ~ .tab-strip .tab[for="tab-analysis"],
#tab-cite:checked     ~ .tab-strip .tab[for="tab-cite"] {
  color: var(--ink);
  border-bottom-color: var(--edtx);
}

/* Keyboard focus must be visible on the LABEL, since the input itself is
 * off-screen. Without this the tabs are reachable but invisible to tab through.
 *
 * EVERY TAB HAS TO BE NAMED IN BOTH BLOCKS. They are id selectors, so a tab
 * added without touching them is reachable, selectable and completely
 * unmarked — no underline saying which view you are on, and no focus ring at
 * all for anyone navigating by keyboard. That is what adding the citation tab
 * did until this line, and nothing in the test suite looks at it. */
#tab-text:focus-visible     ~ .tab-strip .tab[for="tab-text"],
#tab-analysis:focus-visible ~ .tab-strip .tab[for="tab-analysis"],
#tab-cite:focus-visible     ~ .tab-strip .tab[for="tab-cite"] {
  outline: 2px solid var(--navy);
  outline-offset: -2px;
  border-radius: 3px;
}

.tab-panel { display: none; }
#tab-text:checked     ~ .panel-text,
#tab-analysis:checked ~ .panel-analysis,
#tab-cite:checked     ~ .panel-cite { display: block; }

/* Long-form reading. The measure is capped in ch so it tracks the font rather
 * than the viewport: legal prose at full container width is exhausting, and
 * these documents run to 40 pages. */
.panel-text p {
  font-size: 16px;
  line-height: 1.7;
  color: #2a3038;
  max-width: 68ch;
  margin: 0 0 1.35em;
  text-indent: 1.6em;
}

/* Both signals, deliberately. A gap alone was 1.15em against a 1.7 line height
 * — less than one blank line — so consecutive paragraphs read as one unbroken
 * column and the reader had nothing to aim at. An indent alone would be the
 * printed convention, but paragraph detection here is inferred from wrap width
 * rather than given, so a paragraph that reflow got wrong should still be
 * visibly a paragraph. The indent is what a court opinion looks like on paper;
 * the gap is what makes it scannable on a screen. */

/* Structural lines are set flush: an indent on a heading or a byline reads as
 * damage rather than as typography. Nor after a heading, where the break in
 * the page already says a new section has started. */
/* .panel-text p (0,1,1) outranks .auto-note (0,1,0), so the extraction notice
 * was being set at full body size in the reading panel — the one place it most
 * needs to stay a caption. Same declarations it asks for, at a weight that
 * reaches. */
.panel-text .auto-note { font-size: 12.5px; color: var(--ink-faint); }

.panel-text .auto-note,
.panel-text .text-head,
.panel-text .text-byline,
.case-caption p,
.case-front p,
.curated-summary p { text-indent: 0; }
.text-head + p,
.text-byline + p { text-indent: 0; }

/* Section headings — DISCUSSION, BACKGROUND, CONCLUSION. The court sets these
 * apart; rendered as body text they were invisible, and a 40-page opinion with
 * no visible structure is a wall. */
.panel-text .text-head {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ink);
  margin: 2em 0 0.9em;
}
.panel-text .text-head:first-child { margin-top: 0; }

/* The authoring judge. This line is where the opinion actually begins, which
 * is worth marking, because everything above it is cover matter. */
.panel-text .text-byline {
  font-family: var(--serif);
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 1.35em;
}

/* Cover matter: court appealed from, decision date, counsel, panel. Hundreds of
 * words of it, and none of it is the opinion. Quieter and tighter, so the eye
 * can skip it and still find it when it is what the reader wants. */
.case-front {
  max-width: 68ch;
  margin: 0 0 22px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.case-front p {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0 0 8px;
}
.case-front p:last-child { margin-bottom: 0; }

.panel-analysis p { font-size: 15px; line-height: 1.65; max-width: 68ch; }
.analysis-soon { color: var(--ink); }


/* A hand-written editorial summary, kept above the full text on the few
 * documents that have one. Set apart so it never reads as the court's words. */
.curated-summary {
  border-left: 3px solid var(--navy);
  padding: 2px 0 2px 16px;
  margin: 0 0 26px;
  max-width: 68ch;
}
.curated-summary h2 { font-size: 15px; margin: 0 0 8px; }

/* Footnotes, lifted out of the page flow and set at the end where they belong.
 * Left inline they interrupt a sentence mid-argument, because the note lives at
 * the foot of whichever page the sentence happened to break across. */
.fn-ref { font-size: 0.72em; line-height: 0; vertical-align: super; }
.fn-ref a { text-decoration: none; padding: 0 1px; }
.footnotes {
  margin-top: 34px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  max-width: 68ch;
}
.footnotes h2 { font-family: var(--serif); font-size: 15px; margin: 0 0 10px; }
.footnotes ol { padding-left: 22px; margin: 0; }
.footnotes li { font-size: 14px; line-height: 1.6; color: var(--ink-soft); margin-bottom: 8px; }
.fn-back { text-decoration: none; margin-right: 4px; }
/* A jumped-to footnote should be obvious, not left for the reader to find. */
.footnotes li:target { background: #fdf6e3; border-radius: 3px; }

/* The case caption: party names, "v.", docket, counsel. A court sets these as
 * short stacked lines, so full paragraph leading turns them into a ladder of
 * disconnected fragments. Tighter, and slightly quieter than the opinion body. */
.case-caption { margin: 0 0 22px; max-width: 68ch; }
.case-caption p {
  margin: 0 0 2px;
  line-height: 1.45;
  font-size: 15px;
  color: var(--ink-soft);
}
.case-caption p:last-child { margin-bottom: 0; }

/* ---------- Opinion text, geometry-aware extractor (pass 4) ---------- */

/* Serif at a ~38em measure. These documents run to fifty pages; a line length
 * tuned for a feed card is punishing over that distance. */
.opinion {
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.62;
  color: #2a3038;
  max-width: 38em;
  position: relative;
}
.opinion p { margin: 0 0 1.15em; text-indent: 1.6em; }
.opinion h2, .opinion h3 { font-family: var(--serif); margin: 1.9em 0 0.7em; }
.opinion h2 { font-size: 16px; letter-spacing: 0.04em; }
.opinion h3 { font-size: 15.5px; font-weight: 600; }
.opinion h2 + p, .opinion h3 + p, .opinion blockquote + p { text-indent: 0; }

/* The court's own front matter — parties, counsel, panel. Quieter, and never
 * indented: these are stacked lines, not prose. */
.op-front { margin: 0 0 1.6em; padding-bottom: 1.2em; border-bottom: 1px solid var(--line); }
.op-front p { font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); margin: 0 0 6px; text-indent: 0; }
.opinion .byline { font-weight: 600; color: var(--ink); text-indent: 0; }

/* A block quote is set in, tighter, and NOT indented on its first line —
 * the inset is already saying what an indent would say. */
.opinion blockquote {
  margin: 0 0 1.15em 2em;
  font-size: 16px;
  line-height: 1.5;
}
.opinion blockquote p { text-indent: 0; }

/* A list the court set with bullets. list-style:none because the marker the
 * court typed is still the first character of the item — we do not delete the
 * court's glyph so a stylesheet can draw its own. The hanging indent is what
 * makes that read as a list: the marker sits in the gutter and the wrapped
 * lines align under the text, exactly as in the PDF. */
.opinion .op-list, .opinion .op-list ul {
  list-style: none;
  margin: 0 0 1.15em;
  padding: 0 0 0 2em;
}
.opinion .op-list ul { margin: 0.5em 0 0; }
.opinion .op-list li {
  font-size: 16px;
  line-height: 1.7;
  color: #2a3038;
  max-width: 66ch;
  margin: 0 0 0.5em;
  padding-left: 1.4em;
  text-indent: -1.4em;
}
.opinion .op-list li:last-child { margin-bottom: 0; }

/* Star pagination in the gutter, Westlaw-style. Absolute against .opinion so
 * the number sits outside the measure and never interrupts the line. */
.opinion .star {
  position: absolute;
  left: -3.2em;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-faint);
  text-decoration: none;
  padding: 0 3px;
  /* TEXT-INDENT INHERITS, AND THIS IS A BLOCK BOX. The paragraphs carry a
   * 25.6px first-line indent, so every star inherited it and applied it to
   * its OWN first line: the glyphs were pushed 25.6px right, out of the
   * gutter and against the text they mark, and the shrink-to-fit box grew
   * from 18.6px to 44.2px to hold the indent it would never use. The one
   * star that looked right sat in the opening paragraph, which is the one
   * paragraph with no indent to inherit.
   *
   * FIXED WIDTH, RIGHT ALIGNED, so "*7" and "*247" end at the same place
   * instead of starting at it — a column of page numbers reads as a column
   * only if its right edge is straight. 2.6em clears three digits at this
   * size and leaves 6.6px between the box and the measure. */
  text-indent: 0;
  width: 2.6em;
  text-align: right;
}
.opinion .star::before { content: "*"; opacity: 0.65; }
.opinion .star:hover, .opinion .star:focus-visible { color: var(--navy); }
.opinion .star:target { background: #fdf6e3; border-radius: 3px; }

/* Under 46rem there was no gutter to put it in, so it CAME INLINE — and
 * inline means it sits wherever the word before it ended. Measured on an
 * iPhone SE viewport, eight page numbers in one opinion landed at x = 24, 50,
 * 100, 153, 159, 165, 197 and 299: not page markers any more, just digits
 * loose in the prose. A reader reported it, and the desktop gutter is exactly
 * why it looked wrong — there the same eight all sit at 249.
 *
 * So make the gutter instead of giving it up. 1.9rem is about 26px against an
 * 11px star, wide enough for "*12" and for the four-digit case that does not
 * occur in this corpus. It costs the measure 26px of 327, eight per cent, and
 * that is the honest price of a page number that means anything: a quotation
 * taken off a phone still has to carry the page it came from. */
/* WHERE THE PAGE MARGIN RUNS OUT, PUT THE GUTTER INSIDE THE ARTICLE.
 *
 * The rule above hangs the star 3.2em into the page margin, which only works
 * while there IS one. `.wrap.opinion-body` is 760px centred with 24px of
 * padding, so the article's left offset is max(24, (viewport - 760) / 2) —
 * that reaches the 36px the star needs at a 832px viewport and not before.
 * The breakpoint was 46rem (736px), so between 736 and 832 the number hung
 * off the left edge of the page: measured at 744px, the box started at -11.
 *
 * 56rem = 896px leaves 68px of margin, which is clearance rather than a
 * boundary case. If the measure changes, this number is (760 + 2 * 36) / 16
 * rounded up, and it has to change with it. */
@media (max-width: 56rem) {
  /* The gutter has to clear the star's BOX, not its glyphs: at 1.9rem the
   * number ran to 62 while the text began at 54, so "*3" printed through the
   * "F" of the line it marks. The width and the right alignment come from the
   * base rule; only the position and the size of the gutter change here. */
  .opinion { padding-left: 2.6rem; }
  .opinion .star { left: 0; width: 2rem; padding: 0; }
}

/* Three tabs at the desktop size measure 319px, plus 8px of gaps, inside the
 * 327px an iPhone SE leaves — equal to the pixel, so the last label wrapped
 * and the strip grew to two lines of chrome above the opinion. The labels are
 * the labels; the padding is what gives.
 *
 * Its own breakpoint, and a much narrower one than the gutter above: the strip
 * fits comfortably from about 400px up, and shrinking the tabs on a tablet
 * that has room for them would be fixing a phone's problem everywhere. */
@media (max-width: 26rem) {
  .tab { font-size: 14px; padding: 9px 10px; }
}

/* Citations. A long opinion carries hundreds of these, so styling them like
 * ordinary links would turn a page of judicial prose into a page of blue —
 * the reading experience has to survive the linking. They keep the body
 * colour and carry a faint underline that only asserts itself on hover, which
 * is how a printed reporter marks a cite: visible when you look for it,
 * invisible when you are reading past it.
 *
 * span.cite is the unlinked case: recognised as a citation but not resolvable
 * to a document we can point at. It gets NO visual treatment at all. It is a
 * hook for the citator, not an affordance — a reader must never be invited to
 * click something that does not go anywhere. */
.opinion a.cite {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
.opinion a.cite:hover,
.opinion a.cite:focus-visible {
  color: var(--navy);
  text-decoration-color: currentColor;
}

/* A cite that resolves to an opinion we host. Worth distinguishing — it is the
 * one link on the page that keeps the reader inside the corpus — but only just:
 * a solid rule instead of a faint one, still in the body colour. */
.opinion a.cite-internal { text-decoration-color: var(--ink-faint); }

/* What this opinion decides, above the opinion itself.
 *
 * The body text is the court's and is substantially identical to govinfo's
 * copy of it. This block is the part that is ours — a holding classification,
 * a subject taxonomy, the patents resolved out of the text, the citator's view
 * of what came before and after — and it sits above the text so a reader (and
 * a crawler) meets it first. Every value is a link up to a hub. */
.at-issue {
  margin: 1.4em 0 0;
  padding: 14px 18px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
}
.at-issue-row { display: flex; gap: 12px; padding: 5px 0; font-size: 14px; }
.at-issue-row + .at-issue-row { border-top: 1px solid var(--line); }
.at-issue-row .k {
  flex: 0 0 8.5em;
  font: 600 11.5px/1.7 var(--sans);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-faint);
}
.at-issue-row .v { flex: 1; }
.at-issue-row .v a { display: inline-block; margin: 0 0.5em 2px 0; }
.at-issue-row .more { color: var(--ink-faint); font-size: 13px; }

/* Hub index pages: a long list of links with a count beside each. */
.hub-index { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 34px; }
.hub-index li {
  break-inside: avoid;
  padding: 4px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
  display: flex;
  justify-content: space-between;
  gap: 10px;
}
.hub-count { color: var(--ink-faint); font-variant-numeric: tabular-nums; font-size: 13px; }
@media (max-width: 46rem) {
  .hub-index { columns: 1; }
  .at-issue-row { display: block; }
  .at-issue-row .k { margin-bottom: 2px; }
}

/* A citator section with nothing in it still renders. "Not yet cited" is a
 * real answer to a real question, and a section that appears only sometimes
 * teaches a reader to stop looking for it. */
.citator-empty .citator-note { font-style: italic; }

/* Citator sections — "Cited in N later opinions" and "Authorities". Same
 * shape as .related-docs, because they answer the same kind of question about
 * the document, and a reader should not have to learn two layouts for it. */
.citator { margin: 2.2em 0 0; }
.citator > h2 { font-size: 15px; margin: 0 0 4px; }
.citator-note { font-size: 13px; color: var(--ink-soft); margin: 0 0 10px; }
.citator ul { list-style: none; margin: 0; padding: 0; }
.citator li { padding: 5px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.citator li:last-child { border-bottom: 0; }
.citator .item-date {
  display: inline-block; min-width: 10.5em;
  font: 12px/1.4 var(--sans); color: var(--ink-faint);
}
.citator-more { font-size: 12.5px; color: var(--ink-faint); margin: 8px 0 0; }

/* The most-cited view. The count leads the row because it is what the page is
 * sorted by — a reader scanning down the left edge should see the ranking. */
.most-cited .cite-count {
  display: inline-block; min-width: 3.2em;
  font: 600 13px/1.4 var(--sans); color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.citator-caveat { color: var(--ink-faint); font-size: 13px; }

@media (max-width: 46rem) {
  .citator .item-date, .most-cited .cite-count { display: block; min-width: 0; }
}

/* Claim-construction charts are real tables in the source and stay tables
 * here. They are wider than the measure, so they scroll in their own box
 * rather than pushing the page sideways. */
.op-table { max-width: 100%; overflow-x: auto; margin: 0 0 1.4em; }
.op-table table { border-collapse: collapse; font-size: 14px; font-family: var(--sans); }
.op-table td {
  border: 1px solid var(--line);
  padding: 6px 9px;
  vertical-align: top;
  min-width: 8em;
}

.opinion .fn-ref { font-size: 0.72em; line-height: 0; vertical-align: super; }
.opinion .fn-ref a { text-decoration: none; padding: 0 1px; }

/* ---------------------------------------------------------------------------
 * PRINT
 *
 * What a printed opinion is for: reading it away from a screen, and quoting it
 * afterwards. Everything that serves navigation on a screen -- masthead,
 * breadcrumb, tabs, download button, hub links -- is dead ink on paper, and
 * every hub module is a list of links to pages the reader cannot reach.
 *
 * What survives is the document: title, the identifying metadata, the court's
 * text, its footnotes, the star pagination, and the citation. The star numbers
 * stay in the MARGIN, which is the whole reason they are worth printing: a
 * quotation taken off paper still has to carry the page it came from.
 *
 * The rule this replaces was `.tab-panel { display: block !important }`, which
 * un-hid BOTH panels -- so every opinion printed since the tabs landed has
 * ended with "Analysis is coming soon.", a placeholder for a feature that does
 * not exist, stapled to a judicial opinion.
 * ------------------------------------------------------------------------- */
@media print {
  /* A left gutter wide enough for star pagination to sit outside the measure. */
  @page { margin: 18mm 16mm 18mm 26mm; }

  /* Navigation and hub modules: neither is reachable from paper. */
  .masthead, .breadcrumb, .tab-strip, .doc-tabs > .tab-radio,
  .ribbons, .at-issue, .citator, .related-docs,
  .subscribe-cta, .site-footer, .actions, .filters { display: none; }

  /* The text and the citation. Analysis is a placeholder and must never
   * print. The citation moved INTO a tab of its own, and a tab panel is
   * display:none until its radio is checked — so without naming it here, the
   * one thing a printed opinion is most needed for would come out blank. That
   * is the same failure as the rule this block replaced, which un-hid every
   * panel and stapled "Analysis is coming soon." to a judicial opinion; the
   * lesson is that panels have to be listed, not swept. */
  .panel-analysis { display: none !important; }
  .panel-text, .panel-cite { display: block !important; }

  body { background: #fff; color: #000; }
  .wrap, .opinion-body { max-width: none; margin: 0; padding: 0; }

  .opinion-title { font-size: 15pt; line-height: 1.25; margin: 0 0 6pt; }
  .venue-badge { font-size: 8pt; }
  /* border-radius must be zeroed with the border. Kept, a 2pt left rule on a
     6px-radius box renders as a parenthesis sitting in front of the holding. */
  .holding-banner { border: 0; border-left: 2pt solid #000; border-radius: 0;
                    padding: 2pt 0 2pt 8pt; background: none; font-size: 10pt; }
  .opinion-meta { font-size: 9.5pt; }
  /* Tinted panels cost toner and say nothing on paper that the words do not. */
  .auto-note, .opinion-meta { background: none; border-color: #999; }

  .panel-text p { font-size: 11pt; line-height: 1.5; max-width: none; }
  /* Same size as the body it sits in. An item is prose the court numbered,
     not a caption, and a list left at its screen size prints visibly larger
     than the paragraph above it. */
  .opinion .op-list li { font-size: 11pt; line-height: 1.5; max-width: none; }
  .opinion .op-list li { break-inside: avoid; page-break-inside: avoid; }
  /* The court's own section headings. This rule used to target `.text-head`,
     a class the legacy text pipeline emitted and the geometry extractor never
     did -- it matched nothing on any of 6,699 pages, so section headings have
     been printing at their screen size since the rewrite. */
  .opinion h2, .opinion h3 { font-size: 11pt; margin: 1.4em 0 0.6em; }
  .case-front p { font-size: 9.5pt; }
  .footnotes li { font-size: 9pt; }

  /* Star pagination keeps its gutter position; on screen a narrow viewport
     pushes it inline, and paper is never narrow. Stated explicitly so the
     46rem inline fallback cannot win by media-query order. */
  .opinion { position: relative; }
  .opinion .star {
    position: absolute; left: -18mm; margin-right: 0;
    font-size: 8pt; color: #444;
  }

  /* Links are dead on paper. An opinion carries hundreds of citation anchors,
     so they must read as plain text -- and their URLs must NOT be expanded
     after them, which would put a govinfo address inside every third sentence
     of the court's prose. */
  .opinion a, .opinion a.cite, .opinion a.patent { color: #000; text-decoration: none; }
  .source-link a { color: #000; text-decoration: none; }

  /* Do not strand a heading at the foot of a page, or split a quotation. */
  .opinion h2, .opinion h3, .cite-this h2 { break-after: avoid; page-break-after: avoid; }
  .opinion blockquote, .cite-this { break-inside: avoid; page-break-inside: avoid; }
  .opinion p { orphans: 3; widows: 3; }
}
