/* ============================================================================
   Corrections for the inherited page templates.
   ----------------------------------------------------------------------------
   The token remap re-skinned every legacy rule, but it could not fix layout.
   These are the places where the old structure fought the new system and the
   result read as "old layout, new colours", which is worse than either.

   Loaded after gn.css. Everything here is prefixed html.gn so it also outranks
   the <style id="gn-fx"> block that js/fx.js injects at runtime, which lands
   later in the document than any linked stylesheet.
   ============================================================================ */

/* ------------------------------------------------- the discarded neon world

   js/fx.js still injects the effects layer from the pre-cream design: a
   pointer-following aurora, a glowing scroll rail, and spot glows built from
   #00ff88 / #a855f7. The token sweep never reached them because they live in a
   JavaScript string, not in markup. They are pure decoration from a world this
   site no longer occupies, and the glow shadows are the exact tell the system
   was built to remove.

   Hidden rather than deleted at source, because rebuilding the minified bundle
   would force a cache-busting version change across 2,861 pages for a purely
   cosmetic gain. The dead CSS still ships; removing it from fx.src.js is a
   follow-up, not a blocker. */
html.gn .gn-aurora { display: none !important; }
html.gn .gn-spot::before,
html.gn .gn-spot::after { display: none !important; }
html.gn .gn-magnet { transition: none; }

/* The scroll rail was kept on the argument that it is a real affordance on a
   long article. It is not reading as one: on any page scrolled a little way,
   a 2px blue bar sitting above the header looks like a rendering artefact,
   and it was reported as exactly that — "a random blue line in the header".

   An affordance nobody recognises is decoration with a job title. It also
   appears on every page, not just the long ones it was justified by. Gone. */
html.gn .gn-beam { display: none !important; }

/* --------------------------------------------------------- inherited heroes

   The archive, service, industry and glossary templates centre their hero and
   pad the top by 176px to clear a position:fixed nav. The nav is sticky now, so
   that padding was dead space, and the centring made every page except the
   homepage look like a different site. */
html.gn .hero {
  padding: clamp(32px, 5vw, 56px) var(--gut) clamp(24px, 3.5vw, 40px);
  min-height: 0;
  max-width: var(--max);
  margin: 0 auto;
  text-align: left;
  display: block;
}
html.gn .hero-title,
html.gn .hero-desc,
html.gn .hero-badge,
html.gn .stats-bar,
html.gn .newsletter,
html.gn .newsletter-desc,
html.gn .section-transition { text-align: left; }

html.gn .hero-desc {
  max-width: 60ch;
  margin-left: 0;
  margin-right: 0;
  color: var(--ink2);
  font-size: 16px;
  line-height: 1.55;
}

/* The stat strip is the category's oldest device and it is existing content, so
   it is aligned into the system rather than redesigned: a ruled row, not a
   floating centred card. */
html.gn .stats-bar {
  justify-content: flex-start;
  gap: clamp(24px, 5vw, 56px);
  max-width: var(--max);
  margin: 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--line);
  background: none;
  border-radius: 0;
  flex-wrap: wrap;
}

html.gn .newsletter {
  max-width: var(--max);
  margin: 0;
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 22px 24px;
}

/* 112px of air between the hero and the first section on templates that were
   built for a taller, looser world. */
html.gn .hero + .section,
html.gn .hero + section { padding-top: clamp(28px, 4vw, 48px); }

/* Breadcrumbs read as navigation, not as body copy. */
html.gn .breadcrumb,
html.gn .breadcrumbs {
  font-size: 13px;
  color: var(--ink3);
  margin-bottom: 18px;
}
html.gn .breadcrumb a:hover,
html.gn .breadcrumbs a:hover { color: var(--ac); }

/* ---------------------------------------------------------------- eyebrows

   179 pages put a kicker above the heading: "The Problem", "Our Approach",
   "// Diagnose Your Growth". It is the most reliable tell of a generated
   layout, and it never earns its place — the heading already says what the
   section is, so the label is the same sentence twice at two sizes.

   Hidden rather than cut from markup: display:none removes it from the
   accessibility tree as well as the page, and it keeps this reversible in one
   line if a section is later found to depend on one. */
html.gn .section-label { display: none; }

/* With the kicker gone the heading needs its own air, since it was previously
   spaced against the label rather than against the section edge. */
html.gn .section-title { margin-top: 0; }

/* ------------------------------------------------------- article scan block

   Every Signal article and glossary term used to end in related links, which
   is a dead end: the reader learns something and leaves. This turns the end of
   each of 738 pages into an entry point for the scan.

   It is a real GET form pointing at "/", so it works with JavaScript disabled;
   the homepage reads ?scan= and runs immediately. No modal, no email gate, no
   interruption mid-read — it sits where the article finishes. */
html.gn .gn-cta-scan {
  margin: 40px 0 0;
  padding: 22px 22px 24px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--bg2);
}
html.gn .gn-cta-scan h3 {
  font-size: clamp(18px, 2.4vw, 22px);
  font-weight: 540;
  letter-spacing: -.022em;
  margin: 0 0 6px;
}
html.gn .gn-cta-scan p {
  font-size: 14.5px;
  color: var(--ink2);
  line-height: 1.5;
  margin: 0 0 14px;
  max-width: 56ch;
}
html.gn .gn-cta-scan form { display: grid; gap: 8px; max-width: 460px; }
@media (min-width: 520px) { html.gn .gn-cta-scan form { grid-template-columns: 1fr auto; } }
html.gn .gn-cta-scan input {
  height: 46px;
  min-height: 46px;
  padding: 0 14px;
  font-size: 16px;
  border: 1px solid var(--line2);
  border-radius: 10px;
  background: var(--bg);
  width: 100%;
}
html.gn .gn-cta-scan input:focus { border-color: var(--ac); box-shadow: 0 0 0 3px var(--actint); outline: none; }
html.gn .gn-cta-scan button {
  height: 46px;
  min-height: 46px;
  padding: 0 18px;
  border: 0;
  border-radius: 10px;
  background: var(--ac);
  color: var(--acink);
  font-size: 15px;
  font-weight: 530;
  cursor: pointer;
  white-space: nowrap;
}
html.gn .gn-cta-scan button:hover { background: var(--ac); color: var(--acink); opacity: .92; }
html.gn .gn-cta-scan small {
  display: block;
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--ink3);
}

/* ----------------------------------------------------------- article body

   The article header sits on the page gutter while .post-content centres
   itself in an 800px column, so the h1 started at 34px and the first paragraph
   at 204px. One page, two left edges.

   Aligned to the header and given a real reading measure instead. 68ch is the
   upper end of comfortable for 15px Inter; the old 800px column was running to
   roughly 95 characters. */
html.gn .post-content,
html.gn .article-body,
html.gn .glossary-body {
  margin-inline: 0;
  padding-inline: var(--gut);
  max-width: calc(68ch + 2 * var(--gut));
}
html.gn .post-content > *,
html.gn .post-section { max-width: 68ch; }

/* The same two-left-edges bug, still live on all 568 Signal articles, which
   use `.article-content` rather than `.post-content` and so were never covered
   by the rule above.

   The two containers were never on the same rail. Measured at 1440px:
   `.article-header` is max-width 1000 centred with 4rem of padding, so the
   headline starts at x=284; `.article-content` is max-width 750 centred with
   2.5rem, so the body starts at x=385. A hundred pixels of jog between a
   headline and the paragraph under it, on every article on the site.

   Putting the body on the header's box aligns them at every breakpoint —
   the padding steps match the header's own 1.25 / 2.5 / 4rem — and the prose
   keeps its reading measure from the cap on the children rather than from a
   narrower container that had drifted out of alignment. */
html.gn .article-content {
  max-width: 1000px;
  margin-inline: auto;
  padding-inline: 1.25rem;
}
@media (min-width: 768px)  { html.gn .article-content { padding-inline: 2.5rem; } }
@media (min-width: 1024px) { html.gn .article-content { padding-inline: 4rem; } }
/* 58ch, not 68. `ch` is the width of a zero, not of an average character:
   68ch measured 85 real characters here, over the 80 the eye can track, and
   this is the second time that unit has caught me on this codebase. 58ch
   lands near 73 characters and matches the cap the rest of the site uses. */
html.gn .article-content > * { max-width: 58ch; }

/* Note for the next person reading an orphan-class report: `.gn-article` is
   not an unstyled content wrapper. It sits on the <html> element as a
   page-type hook, alongside `gn-page` and `gn-spec`, and is correctly
   styleless — `body` sets the family, so its computed "Times" is the document
   default and nothing inherits it. `.article-author` is likewise not orphaned;
   it carries an inline style attribute a class-based scan cannot see. Both
   showed up as missing styles and neither is. */

/* The scan block is a panel rather than prose, so it gets the full measure. */
html.gn .gn-cta-scan { max-width: none; }

/* ---------------------------------------------------------------------------
   Press clippings.

   These are screenshots of the real articles, cropped to headline, byline and
   date. Nothing else: no body copy, no licensed photography, no advertising
   that happened to be on the page that day.

   The clippings come from publications with different grounds — Benzinga and
   Yahoo Finance are dark, Nasdaq and Entrepreneur are light — and that is left
   alone. Normalising them would make them look like artwork we made up, which
   is the opposite of the point. The card border does the unifying instead.
--------------------------------------------------------------------------- */
html.gn .gn-press {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
  max-width: 1100px;
  margin: 0 auto 1rem;
  padding: 0;
  list-style: none;
}
@media (min-width: 700px) { html.gn .gn-press { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1080px) { html.gn .gn-press.three { grid-template-columns: repeat(3, 1fr); } }

html.gn .gn-clip {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg);
  text-decoration: none;
  color: inherit;
  transition: border-color .18s ease, transform .18s ease;
}
html.gn .gn-clip:hover,
html.gn .gn-clip:focus-visible {
  border-color: var(--ac);
  transform: translateY(-2px);
  color: inherit;
}
@media (prefers-reduced-motion: reduce) { html.gn .gn-clip { transition: none; } html.gn .gn-clip:hover { transform: none; } }

/* Natural height, deliberately.
   A fixed aspect-ratio with object-fit:cover looked tidier but cropped the
   bottom of the taller clippings, and the bottom of a clipping is where the
   byline lives — the one thing these images exist to prove. Ragged card
   heights are a cheap price for never cutting "BY SCOTT MCGOVERN" off.
   Intrinsic width and height attributes on every clipping keep this from
   costing any layout shift. */
html.gn .gn-clip img {
  display: block;
  width: 100%;
  height: auto;
  border-bottom: 1px solid var(--line);
  background: var(--bg3);
}

html.gn .gn-clip .gn-clip-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .3rem .6rem;
  padding: .7rem .85rem .8rem;
}
/* 10.88px and 10.56px, under the 11px legibility floor the rest of the system
   holds to. These two are the outlet and the date on every press citation —
   the only evidence the page offers for its own authority, so they are the
   last text that should be hard to read. Fixed here rather than in gn.css:
   this sheet loads after that one, so an equal-specificity rule there loses
   the tie and changes nothing. */
html.gn .gn-clip .gn-pub {
  font-family: var(--mo);
  font-size: max(12px, .75rem);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink);
}
html.gn .gn-clip .gn-when {
  font-family: var(--mo);
  font-size: max(12px, .75rem);
  letter-spacing: .06em;
  color: var(--ink3);
}
html.gn .gn-clip .gn-read {
  margin-left: auto;
  font-size: .74rem;
  font-weight: 600;
  color: var(--ac);
  white-space: nowrap;
}
html.gn .gn-clip:hover .gn-read { text-decoration: underline; text-underline-offset: .18em; }

html.gn .gn-press-group {
  font-family: var(--mo);
  font-size: max(12px, .75rem);
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ink3);
  text-align: center;
  margin: 2.5rem 0 1.1rem;
}

/* ---------------------------------------------------------------------------
   Profile page: one content column.

   The inherited page CSS gives .section and .section-dark different boxes, and
   .section-dark > * adds its own padding on top. Measured at 1440px, the four
   kinds of child in a dark section started at x=130, x=40, x=170 and x=270 —
   four left edges in one section, which reads as sloppiness rather than
   rhythm. These rules put every direct child of a section on this page into
   the same 1100px column so there is exactly one left edge on the page.

   Scoped to .gn-profile so no other page inherits the correction.
--------------------------------------------------------------------------- */
html.gn .gn-profile .section > *,
html.gn .gn-profile .section-dark > * {
  max-width: 1100px;
  margin-inline: auto;
  padding-inline: 0;
}

/* Prose keeps the shared left edge and caps its measure on the right, rather
   than shrinking its box and drifting out of alignment with the headings. */
html.gn .gn-profile .section-desc {
  max-width: 1100px;
  padding-right: max(0px, calc(1100px - 68ch));
}

html.gn .gn-profile .gn-press { max-width: 1100px; }

/* ------------------------------------------------------- free-tools nav row */
/* The filter row on /free-seo-tools/ renders as seven pills carrying one class,
   .tools-nav-btn — but four are <button> (scroll to a section) and three are
   <a> (leave for another page). That split was visible: the buttons came out
   Public Sans 15px/400, the links JetBrains Mono 12.8px/500, side by side in
   the same row.

   Nothing set out to do that. gn.css has

     html.gn :where(button, input, select, textarea) { font: inherit }

   which is the right reset for form controls — but :where() contributes no
   specificity, so the rule scores (0,1,1) on `html.gn` alone and outranks a
   plain component class at (0,1,0). It also uses the `font` shorthand, so it
   resets family, size, weight and line-height together. <a> is not in the list,
   so the links kept the legacy component styling and the buttons did not.

   Rather than fight that per-element, the row gets one deliberate treatment for
   both tags. It follows .proof-tag, which is this system's pill: 12.5px, sans,
   hairline border, full radius, sentence case. The legacy rule set mono and
   uppercase, which is the eyebrow treatment (.section-label) and reads as a
   different component sitting in the middle of the page. */
html.gn .tools-nav { gap: 8px; }

html.gn .tools-nav-btn {
  font-family: var(--f);
  font-size: 12.5px;
  font-weight: 450;
  letter-spacing: 0;
  line-height: 1.2;
  text-transform: none;
  /* min-height rather than padding alone: the last pill wraps onto a row of its
     own, where there is no sibling to stretch against, and it was rendering
     41px tall next to the 44px of the row above. */
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  padding: 0 13px;
  color: var(--ink2);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}

html.gn .tools-nav-btn:hover,
html.gn .tools-nav-btn.active {
  color: var(--ac);
  border-color: var(--ac);
  background: var(--actint);
}

html.gn .tools-nav-btn:focus-visible {
  outline: 2px solid var(--ac);
  outline-offset: 2px;
}

/* ----------------------------------------------------- legacy form labels */
/* 91 selectors across the legacy template bundles ask for
   `font-family: 'JetBrains Mono', monospace`, and that font has never been in
   /fonts/ — the directory holds newsreader, publicsans, outfit and inter, and
   gn.css declares @font-face for exactly two faces, neither of them this one.
   Every one of those rules therefore falls through to the generic `monospace`
   keyword.

   Measured on macOS, `monospace`, `ui-monospace`, `SF Mono` and `Menlo` all
   resolve to the same face and render at identical widths, so this costs
   nothing here. It is a cross-platform consistency risk rather than a live
   bug: on Windows those rules land on Consolas while gn.css's own --mo token
   asks for ui-monospace first, so the two can diverge. Left alone deliberately
   — an @font-face mapping the name to local() sources was tried and failed to
   load (status "error"), and rewriting 91 selectors spread across 76
   immutable, hash-named bundles is not worth it for a difference nobody on
   this platform can see.

   What did need fixing is below: form labels are not a monospace context. The
   rebuilt scan form on the homepage sets its labels ("Customer value",
   "Monthly visitors", "Close rate") in the body face; the legacy tool pages set
   theirs in mono, so the same control read as two different components
   depending on which page you landed on. 64 of them on /free-seo-tools/. */
html.gn .form-label {
  font-family: var(--f);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink3);
}

/* -------------------------------------------- section prose, left edge kept */
/* On a wide desktop the intro paragraph under a section heading floated into
   the middle of the page while the heading and the card grid stayed left. At
   1920 the heading sat at x=370 and the paragraph at x=705 — a 335px indent
   nobody asked for, with an empty pocket under the heading.

   The cause is two correct rules meeting badly. gn.css centres each child of a
   full-bleed band so they line up on a shared column:

     html.gn .section-dark > * { max-width: var(--max); margin-inline: auto; }

   and separately caps prose to a readable measure:

     html.gn main .section-desc { max-width: 52ch; }

   The second wins on max-width, so the paragraph's *box* shrinks to 52ch — and
   `margin-inline: auto` then centres that small box inside the band instead of
   the 1180px column. Every other child keeps the column width, so only the
   prose drifts.

   The fix is the one already written for .gn-profile further up this file:
   prose keeps the shared left edge and caps its measure on the right, rather
   than shrinking its box and drifting out of alignment with the headings.

   min(100%, var(--max)) is the element's real box width in both regimes — the
   column's 1180px on a wide screen, the container's own width once the screen
   is narrower than that — so the right padding lands the text at exactly 52ch
   in both. Below about 700px the subtraction goes negative and max() falls back
   to the inline padding the band already had, leaving small screens untouched. */
html.gn .section-dark > .section-desc,
html.gn .section > .section-desc {
  max-width: var(--max);
  padding-right: max(4rem, calc(min(100%, var(--max)) - 4rem - 52ch));
}

/* ---------------------------------------------------------------------------
   Article tables.

   Added 2026-08-18. Until now no Signal article used a <table> — the only
   table rule on the site was an orphaned `.comparison-table tbody` that no
   page referenced. A comparison rendered as prose forces both the reader and
   any passage-extraction system to reconstruct the comparison, so the markup
   is worth having; it just needed styling before it could be used.

   The wrapper is not optional. A three-column table at 320px would otherwise
   push the whole page sideways, and horizontal scroll on the document is the
   one layout failure this site does not ship. Scrolling INSIDE .table-scroll
   is fine; scrolling the page is not. scripts/check-article-quality.mjs fails
   any article whose <table> is missing the wrapper, so this cannot regress
   quietly.

   min-width on the table is what makes the wrapper do anything: without it the
   table would compress to the container and the cells would wrap to single
   characters rather than scroll. 34rem is the narrowest width at which a
   three-column row still reads. */
html.gn .article-content .table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 2rem 0;
  border: 1px solid var(--border, rgba(255, 255, 255, 0.12));
  border-radius: 10px;

  /* Scroll affordance. On a phone the table shows two of three columns and
     there is otherwise nothing telling the reader the rest exists — a scroll
     region with no visible edge reads as a truncated table. These are two
     background layers: a cover painted in the surface colour pinned to each
     edge, and a shadow behind it that only becomes visible once the content
     scrolls out from under the cover. Pure CSS, so it works before any script
     runs and cannot get out of sync with the scroll position. */
  background:
    linear-gradient(to right, var(--bg, #fff) 30%, rgba(255, 255, 255, 0)) left center,
    linear-gradient(to left, var(--bg, #fff) 30%, rgba(255, 255, 255, 0)) right center,
    radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.14), rgba(0, 0, 0, 0)) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.14), rgba(0, 0, 0, 0)) right center;
  background-repeat: no-repeat;
  background-size: 44px 100%, 44px 100%, 14px 100%, 14px 100%;
  background-attachment: local, local, scroll, scroll;
}

html.gn .article-content .table-scroll table {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  font-size: 0.95rem;
  margin: 0;
}

html.gn .article-content .table-scroll th,
html.gn .article-content .table-scroll td {
  padding: 0.75rem 1rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border, rgba(255, 255, 255, 0.1));
}

/* Derived from --text at reduced opacity rather than set to --text-muted.
   --text-muted (#6E737C) over this header's tinted background measures 4.43:1,
   which is under the 4.5:1 AA floor for text this size — close enough to look
   fine and still a fail. Deriving from --text lands near 7:1 and keeps working
   if the palette ever changes, which a hardcoded hex would not. */
html.gn .article-content .table-scroll thead th {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text, #08090A);
  opacity: 0.74;
  background: rgba(127, 127, 127, 0.06);
  white-space: nowrap;
}

html.gn .article-content .table-scroll tbody tr:last-child td {
  border-bottom: 0;
}

html.gn .article-content .table-scroll tbody tr:hover td {
  background: rgba(127, 127, 127, 0.05);
}

/* First column carries the row's subject in every table written so far, and
   reads better held apart from the values beside it. */
html.gn .article-content .table-scroll tbody td:first-child {
  font-weight: 600;
}
