  :root {
    --ink: #1c1f26;
    --ink-soft: #565d6b;
    --paper: #faf9f6;
    --panel: #ffffff;
    --line: #e4e1da;
    --accent: #1c1f26;
    --accent-contrast: #faf9f6;
    --error: #b3261e;
    --error-bg: #fdecea;
    --radius: 14px;

    /* Live pattern preview (qr-preview.js) cell colors. Per Joe
       (2026-09-06): the actual word/phrase text stays pure black so it
       reads clearly; everything else -- finder/alignment/timing/format/
       version/dark modules and the fixed noise "cross-hatching" fill --
       is toned down to roughly 90% black / 90% white instead of full
       contrast, so the real text stands out. Starting values, meant to
       be tweaked. Per Joe (2026-09-06), follow-up: the word's own
       background (the "off" cells that sit within the word's actual
       glyph column span, not the hatch fill flanking it) is pure white,
       not the muted --qr-off, so the letters read crisply. */
    --qr-text: #000000;
    --qr-on: #2a2a2a;
    --qr-off: #e6e6e6;
    --qr-text-bg: #ffffff;
  }
  * { box-sizing: border-box; }
  body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
    line-height: 1.5;
  }
  a { color: inherit; }
  .wrap {
    max-width: 1080px;
    margin: 0 auto;
    padding: 0 24px;
  }

  /* Header */
  header.site {
    padding: 28px 0 8px;
  }
  header.site .wrap {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .logo {
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: -0.01em;
  }
  .logo .dot { color: var(--ink-soft); }
  .site-nav a {
    color: var(--ink-soft);
    text-decoration: none;
    font-size: 1.05rem;
    font-weight: 600;
  }
  .site-nav a:hover { color: var(--ink); }

  /* Hero */
  .hero {
    padding: 48px 0 8px;
    display: grid;
    grid-template-columns: 1.1fr 1fr;
    gap: 48px;
    align-items: center;
  }
  .hero h1 {
    font-size: clamp(1.8rem, 3.4vw, 2.6rem);
    line-height: 1.15;
    margin: 0 0 16px;
    letter-spacing: -0.01em;
  }
  .hero p.lede {
    color: var(--ink-soft);
    font-size: 1.05rem;
    max-width: 46ch;
    margin: 0 0 24px;
  }
  .btn {
    display: inline-block;
    border: none;
    border-radius: 999px;
    padding: 12px 22px;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
  }
  .btn-primary {
    background: var(--accent);
    color: var(--accent-contrast);
  }
  .btn-primary:hover { opacity: 0.88; }
  .btn-primary:disabled { opacity: 0.5; cursor: default; }

  /* Slideshow */
  /* Per Joe (2026-09-06): shrink the whole slideshow (image + dots) about
     25% smaller than it was -- it used to fill its whole hero column.
     This has to be a definite `width`, not `max-width`: everything inside
     .slideshow (slides, nav buttons, caption) is position:absolute, so
     the item has ~zero intrinsic content width of its own. A grid item
     with `margin: auto` and no definite width falls back from "stretch to
     fill the column" to "size to content" -- which, combined with that
     near-zero intrinsic width, collapsed this to a couple of pixels
     during testing. An explicit `width` sidesteps that entirely.
     Per Joe (2026-09-08): another ~25% smaller on top of that
     (75% * 0.75 ~= 56%). */
  #examples {
    width: 56%;
    margin: 0 auto;
  }
  .slideshow {
    position: relative;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    aspect-ratio: 1 / 1;
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(28,31,38,0.04);
  }
  .slideshow .slide {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8%;
    opacity: 0;
    /* Per Joe (2026-09-08): slower crossfade between slides (was 0.5s). */
    transition: opacity 0.8s ease;
  }
  .slideshow .slide.active { opacity: 1; }
  .slideshow .slide img {
    width: 100%;
    height: 100%;
    object-fit: contain;
  }
  .slideshow .caption {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 10px 16px 12px;
    font-size: 0.85rem;
    color: var(--ink-soft);
    background: linear-gradient(to top, var(--panel) 55%, transparent);
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }
  .slideshow .nav-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: var(--panel);
    border: 1px solid var(--line);
    width: 34px;
    height: 34px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 1rem;
    color: var(--ink);
  }
  .slideshow .nav-btn:hover { background: var(--paper); }
  .slideshow .nav-prev { left: 10px; }
  .slideshow .nav-next { right: 10px; }
  .dots {
    display: flex;
    justify-content: center;
    gap: 6px;
    margin-top: 12px;
  }
  .dots button {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    border: none;
    background: var(--line);
    padding: 0;
    cursor: pointer;
  }
  .dots button.active { background: var(--ink); }

  /* Shown in place of .generator when JavaScript is disabled -- see the
     <noscript> block around .generator in index.php. Same panel styling
     as .generator itself so it doesn't look out of place taking over
     that spot in the layout. */
  .js-required-notice {
    margin: 72px 0 40px;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 36px;
    text-align: center;
    color: var(--ink-soft);
  }
  .js-required-notice p { margin: 0; }

  /* Generator */
  .generator {
    margin: 72px 0 40px;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 36px;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 40px;
  }
  .generator h2 {
    margin: 0 0 6px;
    font-size: 1.4rem;
  }
  .generator p.help {
    color: var(--ink-soft);
    margin: 0 0 22px;
    font-size: 0.95rem;
  }
  label {
    display: block;
    /* Per Joe (2026-09-08): bigger form text (was 0.82rem). */
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--ink-soft);
    margin-bottom: 6px;
  }
  .field { margin-bottom: 16px; }

  /* Per Joe (2026-09-08): First line / Second line side by side, smaller
     than the full-width fields elsewhere in the form (see the input rule
     scoped to .field-row below). */
  .field-row {
    display: flex;
    gap: 12px;
    margin-bottom: 16px;
  }
  .field-row .field {
    flex: 1;
    min-width: 0;
    margin-bottom: 0;
  }
  .field-row input[type="text"] {
    padding: 8px 10px;
    font-size: 0.9rem;
  }
  .field-row-hint { margin: 6px 0 16px; }

  /* Live pattern preview table -- see qr-preview.js. Fixed physical size
     regardless of mode (37x37 vs 85x85) so switching modes doesn't jump
     the layout around; cell size just comes out smaller at 85x85. */
  .qr-preview-wrap {
    margin: 0 0 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .qr-preview-table {
    /* No width/height here on purpose -- qr-preview.js sizes every <td>
       explicitly (see TABLE_BOX_PX there) and the table's own size just
       follows from that. A <table> with an explicit height but rows that
       don't have one does NOT divide that height evenly in Chrome -- it
       gives all the extra space to the first and last row and collapses
       every row in between to zero, which is what "two big black
       rectangles instead of a QR pattern" turned out to be. */
    border-collapse: collapse;
    background: #fff;
    border: 1px solid var(--line);
  }
  .qr-preview-table td {
    padding: 0;
    margin: 0;
    border: none;
    line-height: 0;
    font-size: 0;
  }
  /* .text = the actual word/phrase being spelled out (full-contrast, so it
     reads clearly). .text-off = the word's own background, within its
     real glyph column span -- pure white so the letters stand out sharply.
     .on/.off = everything else -- structural QR modules and the fixed
     noise "cross-hatching" fill, including the hatch pattern flanking the
     word within the same tier band -- toned down so the real text/word
     area stands out against it. See the --qr-* variables above. */
  .qr-preview-table td.text { background: var(--qr-text); }
  .qr-preview-table td.text-off { background: var(--qr-text-bg); }
  .qr-preview-table td.on { background: var(--qr-on); }
  .qr-preview-table td.off { background: var(--qr-off); }
  .qr-preview-error {
    margin-top: 8px;
    color: var(--error);
    background: var(--error-bg);
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 0.85rem;
    text-align: center;
    max-width: 320px;
  }
  .qr-preview-error[hidden] { display: none; }

  /* Inline error under the Destination URL field -- currently used to
     warn about the '#' character while it's present in the field (per
     Joe, 2026-09-08). Same visual treatment as .qr-preview-error above. */
  .field-error {
    margin-top: 6px;
    color: var(--error);
    background: var(--error-bg);
    border-radius: 8px;
    padding: 6px 10px;
    font-size: 0.85rem;
  }
  .field-error[hidden] { display: none; }

  /* "Ignore 404 errors" checkbox + Help link, under the Destination URL
     field -- per Joe (2026-09-08). Kept as its own row (not just another
     .field-hint) since it holds an actual form control, not just text. */
  .checkbox-field-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
  }
  .checkbox-field {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: 0.9rem;
    font-weight: 400;
    color: var(--ink);
    cursor: pointer;
  }
  .checkbox-field input[type="checkbox"] {
    width: 16px;
    height: 16px;
    margin: 0;
    cursor: pointer;
  }
  .help-link {
    font-size: 0.82rem;
    color: var(--ink-soft);
    text-decoration: underline;
  }
  .help-link:hover { color: var(--ink); }

  input[type="text"] {
    width: 100%;
    padding: 11px 14px;
    border: 1px solid var(--line);
    border-radius: 8px;
    font-size: 1rem;
    background: var(--paper);
    color: var(--ink);
  }
  input[type="text"]:focus {
    outline: 2px solid var(--ink);
    outline-offset: 1px;
  }
  .field-hint {
    /* Per Joe (2026-09-08): bigger form text (was 0.78rem). */
    font-size: 0.88rem;
    color: var(--ink-soft);
    margin-top: 4px;
  }
  /* Per Joe (2026-09-08), follow-up: a couple of specific hints (the
     Second-line character note, and the preview-table caption) bigger
     still than the general .field-hint size above. */
  .field-hint-lg {
    font-size: 1.05rem;
  }
  .preview {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: var(--paper);
    border: 1px dashed var(--line);
    border-radius: 10px;
    min-height: 280px;
    padding: 20px;
    text-align: center;
  }
  .preview .placeholder {
    color: var(--ink-soft);
    /* Per Joe (2026-09-08): bigger (was 0.9rem). */
    font-size: 1.1rem;
  }
  .preview svg {
    max-width: 100%;
    max-height: 320px;
  }
  .preview .error-msg {
    color: var(--error);
    background: var(--error-bg);
    border-radius: 8px;
    padding: 10px 14px;
    font-size: 0.9rem;
  }
  .spinner {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: 3px solid var(--line);
    border-top-color: var(--ink);
    animation: spin 0.8s linear infinite;
  }
  @keyframes spin { to { transform: rotate(360deg); } }

  footer.site {
    padding: 30px 0 50px;
    text-align: center;
    color: var(--ink-soft);
    font-size: 0.82rem;
  }

  @media (max-width: 780px) {
    .hero { grid-template-columns: 1fr; gap: 32px; }
    .generator { grid-template-columns: 1fr; padding: 24px; }
  }
  @media (max-width: 480px) {
    /* First line / Second line: stack instead of side by side once
       there's not enough width for both to stay comfortably usable. */
    .field-row { flex-direction: column; gap: 16px; }
    .field-row .field { margin-bottom: 0; }
  }

  /* FAQ (details/summary) -- per Joe (2026-09-06): faq.html is built out
     of plain <details><summary>Question</summary><p>Answer</p></details>
     blocks; this is the starting style for those. */
  details {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 4px 20px;
    margin-bottom: 12px;
  }
  details summary {
    cursor: pointer;
    padding: 16px 0;
    font-weight: 600;
    list-style: none;
    position: relative;
    padding-right: 28px;
  }
  details summary::-webkit-details-marker { display: none; }
  details summary::after {
    content: '+';
    position: absolute;
    right: 4px;
    top: 16px;
    font-weight: 400;
    color: var(--ink-soft);
  }
  details[open] summary::after { content: '\2212'; }
  details[open] summary { border-bottom: 1px solid var(--line); }
  details > *:last-child { margin-bottom: 0; }
  details p {
    color: var(--ink);
    margin: 16px 0;
  }
