/* =============================================================================
   mehditmimi.com — site skin for the standalone tools
   -----------------------------------------------------------------------------
   The tools were built with their own neutral palette, which made them read as
   bolt-ons next to the main site. This file re-states the tool tokens using the
   values from /css/style.css and adds the site's signature treatments (the
   indigo→purple→pink accent gradient, the glow, the larger radii and the Georgia
   display face) so a tool looks like a page of mehditmimi.com.

   Load it AFTER the tool's own styles.css and toolkit.css — it wins on order.
   ========================================================================== */

/* ---- palette: light (tool default) ------------------------------------- */
:root {
  color-scheme: light;
  --bg: #ffffff;            /* the home page body is white; #f5f5f5 read as colder */
  --surface: #ffffff;
  --surface-2: #f5f5f5;
  --ink: #0a0a0a;
  --muted: #4a4a4a;
  --faint: #6b6b6b;         /* #737373 fell under AA on the page background */
  --line: rgba(0, 0, 0, .10);
  --line-hover: rgba(0, 0, 0, .15);
  /* --line is a DECORATIVE hairline: it separates two things that are already
     legible on their own, and at .10/.14 it measures 1.26:1 light and 1.40:1
     dark. That is right for a panel seam and wrong for the edge of a control,
     because a control whose boundary you cannot see is a control you cannot
     find. This file already knew that — pdf.css draws the signature pad's
     dashed edge at a fixed rgba(10,10,10,.45) with a comment saying --line
     "measured 1.25:1 in light and 1.00:1 in dark, i.e. a literally invisible
     border" — but the fix stayed local to one element. Promote it: --line-strong
     is the hairline for a boundary that CARRIES meaning, tuned to 3:1 against
     --surface-2 (the fill every such control uses) in both themes, which is what
     WCAG 1.4.11 asks of a user-interface component's visual boundary. Deliberately
     not --faint: at 4.7-4.8:1 that is a text colour, and a 2px dashed box drawn
     in text-strength ink reads as content rather than as an empty container. */
  --line-strong: rgba(0, 0, 0, .42);
  --accent: #5b57e8;
  --accent-ink: #ffffff;
  --accent-soft: rgba(99, 102, 241, .10);
  --shadow: 0 2px 8px rgba(0, 0, 0, .06), 0 4px 16px rgba(0, 0, 0, .08);
  --shadow-glow: 0 0 40px rgba(99, 102, 241, .15);

  /* Semantic colour, deliberately NOT the brand ramp: success and failure must
     never be mistaken for "this is a mehditmimi.com control". Neither value is
     new — base64, url-encoder, uuid, regex-tester and jwt-decoder each
     already declared exactly this pair in their own styles.css, and pdf.css and
     img.css hard-coded the same numbers as var() fallbacks plus a hand-written
     dark twin per rule. Declaring them once here makes the convention real: the
     img.css comment at .img-badge records a live bug where one of those dark
     twins was simply forgotten and light-mode red was painted on the dark page.
     Only the two ink colours are promoted. jwt-decoder's --good-soft/--bad-soft
     tints stay local to it: a tint is tuned against the ink that sits ON it, and
     img.css's result badges measure 4.70:1 with their own pair and 4.37:1 —
     under AA — with jwt's, so there is no single right value to hoist. */
  --good: #06744a;                       /* 5.4:1 on white */
  --bad: #b91c1c;                        /* 6.4:1 on white */

  /* site signatures */
  --accent-gradient: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #ec4899 100%);
  /* darkened stops for text/fills that carry small type on light ground */
  --wordmark-gradient: linear-gradient(135deg, #4338ca 0%, #6d28d9 50%, #a21858 100%);
  --cta-gradient: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #be185d 100%);
  --font-display: Georgia, 'Times New Roman', Times, serif;
  --radius-sm: .5rem;
  --radius-md: 1rem;
  --radius-lg: 1.5rem;
}

/* ---- palette: dark ------------------------------------------------------ */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    color-scheme: dark;
    --bg: #0a0a0a;
    --surface: #161616;     /* #131313 was 1.07:1 on the page — panels had no edge */
    --surface-2: #1e1e1e;
    --ink: #f5f5f5;
    --muted: #b8b8b8;
    --faint: #888888;
    --line: rgba(255, 255, 255, .14);
    --line-hover: rgba(255, 255, 255, .2);
    --line-strong: rgba(255, 255, 255, .34);   /* 3:1 on --surface-2 — see the light block */
    --accent: #a5a3fb;
    --accent-ink: #0a0a0a;
    --accent-soft: rgba(139, 92, 246, .18);
    --good: #35c08a;        /* 8.5:1 on --bg */
    --bad: #f0666b;         /* 6.4:1 on --bg */
    --shadow: 0 4px 16px rgba(0, 0, 0, .4), 0 8px 32px rgba(0, 0, 0, .5);
    --shadow-glow: 0 0 40px rgba(99, 102, 241, .3);
    --wordmark-gradient: linear-gradient(135deg, #8b8cf8 0%, #b18cf9 50%, #f472b6 100%);
    --cta-gradient: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #be185d 100%);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0a0a0a;
  --surface: #161616;       /* #131313 was 1.07:1 on the page — panels had no edge */
  --surface-2: #1e1e1e;
  --ink: #f5f5f5;
  --muted: #b8b8b8;
  --faint: #888888;
  --line: rgba(255, 255, 255, .14);
  --line-hover: rgba(255, 255, 255, .2);
  --line-strong: rgba(255, 255, 255, .34);   /* 3:1 on --surface-2 — see the light block */
  --accent: #a5a3fb;
  --accent-ink: #0a0a0a;
  --accent-soft: rgba(139, 92, 246, .18);
  --good: #35c08a;          /* 8.5:1 on --bg */
  --bad: #f0666b;           /* 6.4:1 on --bg */
  --shadow: 0 4px 16px rgba(0, 0, 0, .4), 0 8px 32px rgba(0, 0, 0, .5);
  --shadow-glow: 0 0 40px rgba(99, 102, 241, .3);
  --wordmark-gradient: linear-gradient(135deg, #8b8cf8 0%, #b18cf9 50%, #f472b6 100%);
  --cta-gradient: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #be185d 100%);
}

/* ---- header: the strongest "this is the same site" signal --------------- */
.pg-header {
  /* min-height, not height: the Georgia wordmark is taller and some translations
     are long, so a fixed box clipped or crowded them on small screens. */
  min-height: 64px;
  height: auto;
  padding: 0 clamp(14px, 3vw, 28px);
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
/* No backdrop-filter here: it makes the header a containing block for
   position:fixed descendants, which threw the language and history menus
   hundreds of pixels off-screen on mobile. */
.pg-logo { display: inline-flex; align-items: center; }
.pg-logo img {
  display: block;
  /* the source is 60x60, so 30px CSS is exactly 1:1 at DPR 2 — at 34px it was
     being upscaled on retina and looked soft */
  height: 30px;
  width: 30px;
}
.pg-brand { gap: 14px; }
/* the tool name reads as a wordmark, like the site's display type */
.pg-title {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  /* A darker ramp on light backgrounds: the site gradient's own stops measured
     4.10 / 3.88 / 3.24 against the light header, i.e. below AA at this size. */
  background: var(--wordmark-gradient, var(--accent-gradient));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* long tool names (and translations) must not wrap inside the header */
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pg-brand { min-width: 0; }
.pg-header-actions { gap: 8px; align-items: center; }
.pg-icon-btn {
  position: relative;
  width: 38px; height: 38px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--muted);
  transition: color .25s, border-color .25s, box-shadow .25s, transform .25s;
}
.pg-icon-btn:hover {
  color: var(--ink);
  border-color: var(--line-hover);
  box-shadow: var(--shadow-glow);
  transform: translateY(-1px);
}

/* ---- surfaces: the site uses noticeably softer corners ------------------ */
/* Card-like surfaces only. .pg-preview and .pg-output are full-bleed structural
   panes in the CSS playground — rounding them bit notches out of the layout. */
.rt-panel, .jw-part, .b6-panel, .rt-cheats, .u-panel,
.about-highlight, .about-faq details {
  border-radius: var(--radius-md);
}

/* ---- primary actions carry the site gradient + glow -------------------- */
.about-btn:not(.ghost),
.pg-btn-primary, .u-btn-primary {
  background: var(--cta-gradient, var(--accent-gradient));
  border: 0;
  color: #fff;
  border-radius: var(--radius-md);
  font-weight: 600;
  letter-spacing: .3px;
  box-shadow: 0 4px 15px rgba(99, 102, 241, .4);
}
.about-btn:not(.ghost):hover:not(:disabled),
.pg-btn-primary:hover:not(:disabled), .u-btn-primary:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(99, 102, 241, .5);
}
.about-btn.ghost { border-radius: var(--radius-md); border-color: var(--line); }

/* ---- ...and the off state has to be defined next to the on state -------- *
   Every suite dimmed its disabled primary with `opacity: .5`, which is the one
   thing you must not do to a gradient: half-transparent --cta-gradient over the
   white page resolves to ~#a7a2f2, and the white 700-weight label on it measured
   2.2:1 — unreadable. Worse, it stayed the most saturated object on the screen,
   so on every tool's EMPTY state the loudest thing on the page was the button
   you cannot press yet, and during a run the busy button out-shouted the
   spinner that was reporting progress.

   A disabled control should recede and still be legible. Drop the gradient for
   the recessed surface, keep the label at --faint (5.0:1 light / 4.9:1 dark) and
   replace the coloured drop shadow with an inset hairline so the button keeps
   its shape without projecting off the page. Declared here, once, because this
   is the file that decides what a primary action looks like. */
.about-btn:not(.ghost):disabled,
.pg-btn-primary:disabled, .u-btn-primary:disabled,
.pdf-btn:not(.pdf-btn-ghost):disabled,
.img-btn:not(.img-btn-ghost):disabled {
  background: var(--surface-2);
  color: var(--faint);
  box-shadow: inset 0 0 0 1px var(--line);
  transform: none;
  cursor: not-allowed;
}

/* ---- display type: Georgia, as on the site ----------------------------- */
.tool-about h1, .tool-about .about-hero-h,
.tool-about .about-h2,
.tool-about .about-highlight h2 {
  font-family: var(--font-display);
  letter-spacing: -0.01em;
}
/* the page's own H1 gets the gradient the site gives its headline */
.tool-about h1, .tool-about .about-hero-h {
  background: var(--accent-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* The about section ships its own palette, deliberately independent of the tool
   chrome. That made a visible seam where the two backgrounds met, so point it at
   the same tokens. The selectors mirror the specificity of the tool's own
   :root[data-theme=...] .tool-about rules, and come later, so they win. */
:root .tool-about,
:root[data-theme="light"] .tool-about,
:root[data-theme="dark"] .tool-about {
  --ab-bg: var(--bg);
  --ab-surface: var(--surface-2);
  --ab-text: var(--ink);
  --ab-muted: var(--muted);
  --ab-faint: var(--faint);
  --ab-border: var(--line);
  --ab-accent: var(--accent);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .tool-about {
    --ab-bg: var(--bg);
    --ab-surface: var(--surface-2);
    --ab-text: var(--ink);
    --ab-muted: var(--muted);
    --ab-faint: var(--faint);
    --ab-border: var(--line);
    --ab-accent: var(--accent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .pg-icon-btn, .about-btn { transition: none; }
  .pg-icon-btn:hover, .about-btn:hover { transform: none; }
}

/* ---- brand inside the working area ------------------------------------- *
   The first pass only branded the header and the About section, which left the
   part people actually use looking like a generic utility. On the site the accent
   is everywhere, so carry it onto the controls that show state.               */

/* selected state = the site gradient, not a flat fill */
.rt-flag-toggles button.active,
.b6-seg button.active,
.pg-tab.active,
.pg-mode.active,
.u-seg button.active {
  /* Same reason as the checked pills: white 600-weight text on --accent-gradient
     measures 4.47 / 4.23 / 3.53:1 along its stops, below AA and worst at the pink
     end. --cta-gradient is the darkened ramp already used behind white button
     text and reads as the same colour, at 6.29 / 5.70 / 6.04:1. */
  background: var(--cta-gradient, var(--accent-gradient));
  border-color: transparent;
  color: #fff;
  box-shadow: 0 2px 10px rgba(99, 102, 241, .35);
}

/* active tab underline picks up the gradient too */
.rt-ptab.active,
.pg-codetab.active {
  border-bottom-color: transparent;
  background-image: var(--accent-gradient);
  background-repeat: no-repeat;
  background-size: 100% 2px;
  background-position: 0 100%;
}

/* panels lift toward the accent on hover, the way the site's cards do */
.rt-panel, .jw-part, .b6-panel, .u-panel {
  transition: border-color .25s ease, box-shadow .25s ease;
}
.rt-panel:hover, .jw-part:hover, .b6-panel:hover, .u-panel:hover {
  border-color: var(--line-hover);
}

/* the mobile dropdowns were positioned for the old 56px header */
@media (max-width: 460px) {
  .tk-menu, .tk-hist-panel { top: 72px; }
}

@media (prefers-reduced-motion: reduce) {
  .rt-panel, .jw-part, .b6-panel, .u-panel { transition: none; }
}

/* ---- site footer -------------------------------------------------------- *
   Reaching a tool from a search result should not be a dead end.            */
.tk-site-foot {
  background: var(--surface-2);
  border-top: 1px solid var(--line);
  padding: 40px clamp(18px, 4vw, 32px);
  font-family: var(--sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif);
}
.tk-foot-inner {
  max-width: 860px; margin: 0 auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px 28px;
}
.tk-foot-brand {
  display: inline-flex; align-items: center; gap: 10px;
  text-decoration: none; color: var(--ink); font-weight: 700;
}
.tk-foot-brand img { height: 30px; width: 30px; display: block; }
.tk-foot-name { font-family: var(--font-display); font-size: 1.05rem; }
.tk-foot-nav { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-left: auto; }
.tk-foot-nav a {
  color: var(--muted); text-decoration: none; font-size: .95rem; font-weight: 600;
  padding: 4px 2px; border-bottom: 2px solid transparent; transition: color .2s, border-color .2s;
}
.tk-foot-nav a:hover { color: var(--ink); border-bottom-color: var(--accent); }
.tk-foot-note { flex: 1 0 100%; margin: 0; color: var(--faint); font-size: .85rem; }
@media (max-width: 640px) {
  .tk-foot-nav { margin-left: 0; }
}
@media (prefers-reduced-motion: reduce) { .tk-foot-nav a { transition: none; } }

/* ---- touch targets in the shared header --------------------------------- *
   The header controls are 38x38 and the logo link 30x30, which is under the 44px
   a finger needs — and these are the buttons people reach for most (theme,
   language, back to the tools).

   The size is not the thing to change: four 44px buttons plus the logo do not fit
   beside the tool name at 320px, and the title already has to ellipsize there. So
   grow only the hit area, with a pseudo-element that takes up no layout — the same
   approach pdf.css uses for its small row buttons. Buttons sit on a 46px pitch, so
   44px targets leave a 2px gap and never overlap.

   The query is (pointer: coarse), (max-width: 760px) rather than pointer alone:
   emulated mobile viewports still report a fine pointer, so a coarse-only rule is
   untestable — and a narrow window on a laptop is often a phone-sized touch
   surface anyway. */
/* ---- the tool name on a phone ------------------------------------------- *
   nowrap + ellipsis keeps the header tidy on a desktop, but on a phone it left
   the title with ~120px against the 129-201px the French and Spanish names need:
   every tool page rendered as "Déverr…", "Numérot…", "Métadonn…". The name is the
   only wayfinding in the header, so an ellipsis there costs more than a taller
   header — and .pg-header was already min-height/height:auto precisely so it could
   grow. Let it wrap to a second line instead, and only where it has to. */
@media (max-width: 760px) {
  .pg-title {
    white-space: normal;
    /* break-word rather than anywhere. `anywhere` makes the title's min-content
       contribution ONE CHARACTER, which is what let flexbox crush the box below
       the width of a single word; break-word still guarantees an over-long word
       can never overflow. Nothing else about the wrap changes at this width. */
    overflow-wrap: break-word;
    line-height: 1.2;
    font-size: 1.05rem;
  }
  .pg-header { padding-top: 8px; padding-bottom: 8px; }
  .pg-brand { gap: 10px; }
}

/* ---- the header at 320px ------------------------------------------------- *
   Below ~400px the row is genuinely over-subscribed: 20px of padding, a 30px
   logo, four 38px buttons and their gaps leave the tool name about 60px. With
   `min-width: 0` on .pg-title (set upstream so the DESKTOP ellipsis can work)
   flex happily shrinks past even that, and measured at 320 the title box came
   out 54px on every one of the 26 pages, in all three languages, breaking the
   name mid-word: "Comp / ress / Image", "Méta / donné / es PDF", four lines for
   "Numéroter les pages", and a sticky header 77-98px tall to hold the wreckage.

   Restore min-content sizing so the box asks for its longest WORD, and let the
   header row break so the buttons drop underneath instead of squeezing it. The
   name now sets on one line at its natural 142-155px; the header measures 95px,
   i.e. no taller than the broken version it replaces. Scoped to ≤360px on
   purpose: wrapping the header costs 31px of permanently-sticky chrome (64px to
   95px), so it is worth it only where the alternative is a broken word. At 390
   the tighter chrome below is enough on its own.

   The actions gap goes to 6px and no lower: the buttons are 38px, so 6px is a
   44px pitch, exactly the width of the invisible 44px touch target defined at
   the bottom of this file — adjacent targets end up flush and never overlap. */
/* Step one, on every phone: give the name back the ~20px the chrome was holding
   in reserve. Measured at 390 in French, the title box was 124px against the
   143px "Redimensionner" needs, so Resize — and only Resize — still broke
   mid-word; at 144px it no longer does, and that 144 clears the widest single
   word any of the 20 tool names produces in the three languages.
   The actions gap goes to 6px and no lower: the buttons are 38px, so 6px is a
   44px pitch, exactly the width of the invisible 44px touch target defined at
   the bottom of this file — adjacent targets end up flush and never overlap. */
@media (max-width: 460px) {
  .pg-header { padding-left: 10px; padding-right: 10px; column-gap: 12px; row-gap: 10px; }
  .pg-header-actions { gap: 6px; }
  .pg-brand { gap: 8px; }
}
/* Step two, only where step one cannot be enough: at 320 the four buttons alone
   are 170px of a 358px row, so there is no arrangement in which the name and the
   chrome share one line. Break the row instead of the word. */
@media (max-width: 360px) {
  .pg-title { min-width: auto; }
  .pg-header { flex-wrap: wrap; }
  .pg-header-actions { margin-left: auto; }
  /* ...and the two fixed dropdowns are pinned to a viewport offset, not to the
     header, so a header that grew from 64px to 95px would have opened them 23px
     UNDERNEATH it. Measured header bottom at 320 is 95px in all three languages;
     104 leaves them the same ~8px of clearance they have at every other width. */
  .tk-menu, .tk-hist-panel { top: 104px; }
}

@media (pointer: coarse), (max-width: 760px) {
  .pg-icon-btn::after,
  .pg-logo::after {
    content: ''; position: absolute; left: 50%; top: 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
  }
  .pg-logo { position: relative; }
}

/* ---- high-contrast preferences ------------------------------------------ *
   Nothing on this site answered either of these queries. Both are cheap, and
   both matter to people who have explicitly asked their OS for help.

   prefers-contrast: more — the de-emphasised inks are deliberately quiet
   (--faint sits around 4.9:1, which passes AA but is exactly what someone asking
   for more contrast is asking not to have). Pull the greys and the hairlines
   toward the text colour; leave the accent alone, since it is already strong.  */
@media (prefers-contrast: more) {
  :root {
    --muted: #2b2b2b;
    --faint: #3d3d3d;
    --line: rgba(0, 0, 0, .34);
    --line-hover: rgba(0, 0, 0, .5);
    --line-strong: rgba(0, 0, 0, .62);
  }
  :root[data-theme="dark"] {
    --muted: #e6e6e6;
    --faint: #cfcfcf;
    --line: rgba(255, 255, 255, .42);
    --line-hover: rgba(255, 255, 255, .6);
    --line-strong: rgba(255, 255, 255, .7);
  }
}
@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --muted: #e6e6e6;
    --faint: #cfcfcf;
    --line: rgba(255, 255, 255, .42);
    --line-hover: rgba(255, 255, 255, .6);
    --line-strong: rgba(255, 255, 255, .7);
  }
}

/* forced-colors (Windows High Contrast) replaces every colour with a system one,
   so anything that carried meaning ONLY in its colour stops carrying it. Borders
   and outlines survive the substitution, so the distinctions are restated in
   shape. Backgrounds and shadows are dropped by the mode itself. */
@media (forced-colors: active) {
  /* a focus ring drawn as a box-shadow is discarded; outline is not */
  :focus-visible { outline: 3px solid Highlight; outline-offset: 2px; }
  .pg-icon-btn, .about-btn { border: 1px solid ButtonBorder; }
  /* the gradient wordmark clips to transparent text, which disappears entirely */
  .pg-title { forced-color-adjust: none; }
  /* These declare border: 0 and take their shape from a gradient fill, which is
     substituted away — so the primary action ends up with no boundary at all
     (1.4.11). Restated here rather than in each tool, which is where uuid and
     css-playground had each had to patch it locally. */
  .pg-btn-primary, .u-btn-primary,
  .tool-about .about-btn:not(.ghost) { border: 1px solid ButtonText; }
}

/* ---- page hero (shared by every tool page) ---------------------------------------------------------- *
   Every tool opened on a bare paragraph: no heading anywhere above the fold, and
   the only <h1> sat below the tool in the About band. So a visitor arriving from
   a search result landed on a sentence with nothing naming the page, and the top
   third of a wide screen was empty. The hero names the page, carries its icon,
   and gives the column something to start with. */
.tool-hero { margin: 0 0 26px; display: flex; align-items: flex-start; gap: 16px; }
.tool-hero-icon {
  flex: 0 0 auto; width: 54px; height: 54px; border-radius: 14px;
  display: flex; align-items: center; justify-content: center; line-height: 1;
  /* The site draws a card's icon on a TINTED tile in the icon's own colour and no
     hairline — /css/style.css .tool-card-icon is an 80px tile filled with a
     rgba(accent,.15) gradient. These tiles were --surface-2 behind a --line
     hairline, i.e. a grey chip: the one part of the page carrying the brand mark
     was the one part painted in no brand colour at all, and on a light page the
     chip and the card it sat on differed by 3% luminance. --accent-soft is the
     token that already tints the privacy note, so no new colour enters the file.
     The border stays declared (transparent) so forced-colors can still draw one. */
  background: var(--accent-soft); border: 1px solid transparent;
}
/* Stated identically in all eight tool stylesheets before this; font-size does
   nothing to an SVG, so the mark has to be sized here, and the `font-size: 27px`
   that used to sit on the tile was for the emoji that no longer exists. */
.tool-hero-icon svg { width: 27px; height: 27px; color: var(--accent); }
/* In Windows High Contrast a brand hue is the one thing a user has asked NOT to
   see; the mark should take their palette like every other glyph on the page. */
@media (forced-colors: active) {
  .tool-hero-icon svg { color: CanvasText; }
}
.tool-hero-text { min-width: 0; }
.tool-hero h1 {
  /* Up one step from clamp(1.55, 3.2vw, 2.1rem). Measured at 1440 the h1 set at
     33.6px while the About band's headline below it set at 35.2px in the brand
     gradient at weight 800 — the page's own title was the smaller, plainer of the
     two headings on the page. The About headline steps down in pdf.css/img.css;
     this is the other half of that, so the ramp reads 37.6 -> 28 -> 20.8. */
  margin: 0 0 6px; font-family: var(--font-display, Georgia, serif);
  font-size: clamp(1.7rem, 3.4vw, 2.35rem); line-height: 1.12; letter-spacing: -0.02em;
  font-weight: 700; color: var(--ink);
}
/* the lede is the hero's standfirst now, so it loses its own top margin */
.tool-hero .pdf-lede, .tool-hero .img-lede, .tool-hero .tool-hero-lede { margin: 0; }
@media (max-width: 560px) {
  .tool-hero { gap: 12px; }
  .tool-hero-icon { width: 44px; height: 44px; font-size: 22px; border-radius: 12px; }
}

/* ---- the hero on a hub -------------------------------------------------- *
   A tool page's hero is followed immediately by the drop zone, whose dashed edge
   closes the masthead. A hub's is followed by a card wall that starts 26px lower
   and, above 1120px, is 1140px wide against a standfirst measured at 54ch — so
   the hero was ~590px of type with ~550px of nothing beside it and nothing under
   it, floating rather than heading the page. A hairline the full width of the
   card wall gives it a bottom edge, which is the same device the page header and
   the About band already use to separate one region from the next. */
.tool-hero:has(+ .hub-group), .tool-hero:has(+ .pdf-hub), .tool-hero:has(+ .img-hub) {
  margin-bottom: 30px; padding-bottom: 26px; border-bottom: 1px solid var(--line);
}
