/* ═══════════════════════════════════════════════════════════════
   Type Unlimited — specimen.css
   Shared styles for font specimen pages (fonts/<name>/index.html).
   Paired with assets/js/specimen.js — each specimen page supplies
   only its own FONT_CONFIG object and a minimal HTML skeleton.
═══════════════════════════════════════════════════════════════ */

/* ── SPACING SCALE ───────────────────────────────────────────── */

:root {
  --space-xs:   8px;
  --space-s:   16px;
  --space-m:   24px;
  --space-l:   48px;
  --space-xl:  96px;
  --space-2xl: 160px;

  --wght: 400; /* live weight-axis value; set by the weight slider (variable fonts only) */
}

@media (max-width: 640px) {
  :root {
    --space-l:   32px;
    --space-xl:  64px;
    --space-2xl: 96px;
  }
}

/* ── LAYOUT ──────────────────────────────────────────────────── */

.specimen-body {
  /* .cta-block (shared, main.css) always ends the page now and carries its
     own generous bottom padding, sitewide-consistent — no extra gap needed
     here for last section → footer. */
  padding: 0 var(--pad-x);
}

/* ── HERO ────────────────────────────────────────────────────── */

.specimen-hero {
  padding-top: var(--space-l);   /* nav → back link */
}

.specimen-back {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: var(--fg-mid);
  margin-bottom: var(--space-m); /* back link → hero */
  transition: color 0.2s;
}

.specimen-back:hover {
  color: var(--fg);
}

.specimen-hero-name {
  font-size: clamp(72px, 12vw, 180px);
  font-weight: normal; /* this is now an <h1> — override the UA bold default; actual weight is driven by --wght / applyWeight() */
  line-height: 1;
  letter-spacing: 0.01em;
  margin-bottom: var(--space-s); /* hero → description — one step tighter than the scale's default m→s, per hero cluster density request */
  /* font-family set by JS */
}

@media (max-width: 640px) {
  /* Below 600px, 12vw alone would go under the 72px floor above, so a
     longer name (e.g. "NOSTROMO") stops shrinking and overflows its
     container. Drop the floor to 40px here so long names keep scaling
     down with the viewport instead of plateauing. Continuous with the
     base rule at 600px, where 12vw already equals 72px on its own. */
  .specimen-hero-name {
    font-size: clamp(40px, 12vw, 180px);
  }
}

/* Live weight axis — one variable, driven by the weight slider (or its
   breathing animation), consumed by the hero, the tester, the character
   set, and the carousel preview. Variable fonts only; no-op otherwise. */
.specimen-hero-name,
.specimen-textarea,
.glyph-cell,
.carousel-preview-text {
  font-variation-settings: 'wght' var(--wght);
}

.specimen-hero-desc {
  font-family: var(--font-mono);
  font-size: clamp(0.875rem, 1.1vw, 1rem);
  color: var(--fg-mid);
  line-height: 1.6;
  max-width: 560px;
  margin-bottom: var(--space-xs); /* description → tags — one step tighter, same hero cluster density request */
}

/* Purchase button (optional, fonts/js only injects it when FONT_CONFIG.purchase
   is set) — a real .btn-cta-small (buying is this page's actual conversion),
   with a separate price/checkout caption beside it, never inside the filled
   surface — same convention as .cta-block-label sitting beside .btn-cta-big.
   The caption is linked to the button via aria-describedby (set in JS), not
   nested inside the <a>. Row wraps (caption drops under the button) on
   narrow screens via flex-wrap — no separate short-copy variant. */
.specimen-purchase {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: var(--space-m); /* tags → purchase row — one step up from the hero cluster's tighter density, per request */
}

.specimen-purchase-meta {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--fg-mid); /* readable — never --fg-faint, which is a hover-tint token, not a text color */
}

/* Screen-reader-only text — same clip technique as main.css's
   .visually-hidden-input, generalised here for non-input content
   (e.g. an "(opens in new tab)" note on an external link). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── CAROUSEL (multi-slide fonts, e.g. NOSTROMO) ────────────── */

.specimen-carousel {
  margin-top: var(--space-xl); /* section → section — one step tighter, per request */
}

.carousel-slide {
  display: none;
  width: 100%;
}

.carousel-slide.active {
  display: flex;
  align-items: center;
  justify-content: center;
}

.carousel-slide-text {
  flex-direction: column;
  min-height: 160px;
}

.carousel-preview-text {
  font-size: clamp(2rem, 5vw, 4.5rem);
  line-height: 1.2;
  text-align: center;
  word-break: break-word;
  padding: 24px 0;
  /* font-family set by JS */
}

.carousel-slide-image img {
  width: 100%;
  height: auto;
  display: block;
}

.carousel-controls {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 24px;
}

.carousel-chip {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-mid);
  background: none;
  border: 0.5px solid var(--rule);
  padding: 6px 14px;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}

.carousel-chip:hover {
  color: var(--fg);
  border-color: var(--fg);
}

.carousel-chip.active {
  color: var(--bg);
  background: var(--fg);
  border-color: var(--fg);
}

/* Weight breathing animation indicator (variable fonts only). Lives in
   .specimen-preview now, next to the weight control it actually describes —
   previously nested in .specimen-carousel, which hides itself entirely for
   any font with no carousel slides (e.g. NOSTROMO today), silently hiding
   this along with it. */
/* A real button now (was a static status readout), so a visitor can stop
   or resume the ambient weight animation deliberately instead of it only
   ever stopping as a side effect of touching a slider. Stays visible and
   clickable in both states -- unlike the old opacity:0-on-paused version,
   fading it out would hide the only way to resume it. */
.anim-indicator {
  display: flex;
  align-items: center;
  gap: 6px;
  justify-content: flex-start;
  margin-bottom: var(--space-m); /* indicator → type tester */
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--fg-mid);
  background: none;
  border: none;
  padding: 4px 2px;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.25s;
}

.anim-indicator:hover {
  opacity: 1;
}

.anim-indicator:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 2px;
}

.anim-dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--fg);
  animation: pulse 1.8s ease-in-out infinite;
}

.anim-dot:nth-child(2) { animation-delay: 0.3s; }
.anim-dot:nth-child(3) { animation-delay: 0.6s; }

.anim-indicator.paused .anim-dot {
  animation-play-state: paused;
  opacity: 0.3;
}

@keyframes pulse {
  0%, 100% { opacity: 0.3; transform: scale(0.8); }
  50%      { opacity: 1;   transform: scale(1.2); }
}

@media (prefers-reduced-motion: reduce) {
  /* Belt-and-suspenders alongside the JS check in specimen.js that skips
     starting the weight-breathing loop at all when this is set — this
     covers the dots' own CSS animation independently of that JS path. */
  .anim-dot { animation: none; }
}

/* ── GALLERY (static image fonts, e.g. Pops) ────────────────── */

.specimen-gallery {
  margin-top: var(--space-xl); /* section → section — one step tighter, per request */
}

.gallery-header {
  font-family: var(--font-mono);
  font-size: var(--caps-size);
  font-weight: var(--caps-weight);
  letter-spacing: var(--caps-tracking);
  text-transform: uppercase;
  color: var(--fg-mid);
  margin-bottom: var(--space-m); /* section label → content */
}

.gallery-list {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 900px;
}

.gallery-list img {
  width: 100%;
  height: auto;
  display: block;
}

/* ── TYPE TO TEST (controls + live preview, merged) ─────────── */

.specimen-preview {
  position: relative; /* positioning context for .fake-cursor, which lives here rather than inside the contenteditable textarea itself */
  margin-top: var(--space-l); /* hero → Type to test; tighter than the standard section → section gap */
}

.preview-header {
  font-family: var(--font-mono);
  font-size: var(--caps-size);
  font-weight: var(--caps-weight);
  letter-spacing: var(--caps-tracking);
  text-transform: uppercase;
  color: var(--fg-mid);
  margin-bottom: var(--space-m); /* section label → content */
}

.controls-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  margin-bottom: 32px;
}

@media (max-width: 640px) {
  .controls-grid {
    grid-template-columns: 1fr;
    gap: 32px;
  }
}

.control-group {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.control-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--fg-mid);
  letter-spacing: 0.04em;
}

.control-value {
  font-family: var(--font-mono);
  color: var(--fg);
  font-weight: 500;
}

/* Slider — the input's own box is a real 44px hit area (WCAG 2.5.5 AAA
   target size, comfortable for touch); the visible hairline track is
   drawn on the track pseudo-elements instead, so the box is invisible but
   still fully clickable/tappable top-to-bottom. The 1px track stays
   centered in this taller box by default, so the thumb's own centering
   offset below doesn't need to change. */
.specimen-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 44px;
  background: transparent;
  outline: none;
  cursor: pointer;
}

.specimen-slider::-webkit-slider-runnable-track {
  width: 100%;
  height: 1px;
  background: var(--rule);
}

.specimen-slider::-moz-range-track {
  width: 100%;
  height: 1px;
  background: var(--rule);
}

.specimen-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--fg);
  cursor: pointer;
  transition: transform 0.15s;
  margin-top: -7.5px; /* centers the 16px thumb on the 1px WebKit track: (1 - 16) / 2 */
}

.specimen-slider::-webkit-slider-thumb:hover {
  transform: scale(1.25);
}

.specimen-slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--fg);
  cursor: pointer;
  border: none;
}

.weight-track-labels {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--fg-mid);
  margin-top: 4px;
}

/* Weight buttons (static fonts, e.g. Pops) */
.weight-buttons {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.weight-btn {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-mid);
  background: none;
  border: 0.5px solid var(--rule);
  padding: 8px 18px;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}

.weight-btn:hover {
  color: var(--fg);
  border-color: var(--fg);
}

.weight-btn.active {
  color: var(--bg);
  background: var(--fg);
  border-color: var(--fg);
}

.specimen-textarea {
  width: 100%;
  min-height: 180px;
  padding: 24px;
  border: 0.5px solid var(--rule);
  background: transparent;
  color: var(--fg);
  font-size: 36px;
  line-height: 1.4;
  outline: none;
  resize: vertical;
  transition: border-color 0.2s;
  caret-color: transparent; /* native caret hidden -- .fake-cursor (specimen.js) draws it instead */
  /* font-family set by JS */
}

.specimen-textarea:focus {
  border-color: var(--fg);
}

.specimen-textarea:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 0; /* flush against the border, not a separate ring outside it -- a visible gap here read as a distracting double stroke */
}

.specimen-textarea[contenteditable]:empty::before {
  content: "Type here to test the font...";
  color: var(--fg-mid);
  opacity: 0.5;
}

/* Custom blinking text cursor -- replaces the native caret (suppressed via
   caret-color above) so it can keep blinking at a steady, consistent rate
   across browsers rather than each browser/OS's own caret-blink timing.
   Positioned in JS against .specimen-preview (not inside the textarea
   itself, since an element living inside contenteditable content risks
   being selected, typed over, or deleted by the user). Shown only while
   the tester actually has focus -- see specimen.js. */
.fake-cursor {
  position: absolute;
  width: 2px;
  background: var(--fg);
  pointer-events: none;
  display: none;
}

.fake-cursor.visible {
  display: block;
  animation: cursor-blink 1s step-end infinite;
}

@keyframes cursor-blink {
  from, to { opacity: 1; }
  50%      { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .fake-cursor.visible {
    animation: none;
  }
}

/* The tester arrives pre-filled with real copy (see specimen.js), so the
   :empty::before placeholder above never actually shows in practice --
   without this, nothing on the page tells a visitor the text is editable. */
.specimen-tester-hint {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--fg-mid);
  opacity: 0.6;
}

/* ── GLYPH GRID (character set) ─────────────────────────────── */

.specimen-glyphs {
  margin-top: var(--space-xl); /* section → section — one step tighter, per request */
}

/* A lead hook, not just a courtesy: a visitor who needs a character this
   font doesn't have is exactly the kind of custom-type inquiry this site
   exists to catch. Shown under every font's grid regardless of whether
   that font actually has gaps -- true either way, and avoids a stale
   count baked into copy. */
.glyph-missing-link {
  display: inline-block;
  margin-top: var(--space-s);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--fg-mid);
  opacity: 0.7;
  transition: color 0.2s, opacity 0.2s;
}

.glyph-missing-link:hover,
.glyph-missing-link:focus-visible {
  color: var(--fg);
  opacity: 1;
}

.glyphs-header {
  font-family: var(--font-mono);
  font-size: var(--caps-size);
  font-weight: var(--caps-weight);
  letter-spacing: var(--caps-tracking);
  text-transform: uppercase;
  color: var(--fg-mid);
  margin-bottom: var(--space-m); /* section label → content */
}

.glyph-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
  gap: 1px;
}

.glyph-cell {
  background: var(--bg);
  border: 0.5px solid var(--rule);
  display: grid;
  place-items: center;
  overflow: hidden;
  font-size: clamp(1rem, 2vw, 1.5rem);
  aspect-ratio: 1;
  cursor: pointer;
  transition: background 0.15s, transform 0.15s;
  user-select: none;
  color: var(--fg);
  /* font-family set by JS */
}

.glyph-cell.glyph-cell-space {
  font-size: 0.875rem;
  color: var(--fg-mid);
}

.glyph-cell:hover {
  background: var(--fg-faint);
  transform: scale(1.08);
  z-index: 1;
}

.glyph-cell:active {
  transform: scale(0.95);
}

@media (max-width: 640px) {
  .glyph-grid {
    grid-template-columns: repeat(auto-fill, minmax(32px, 1fr));
  }
}

/* ── TECHNICAL DETAILS ───────────────────────────────────────── */

.specimen-tech {
  margin-top: var(--space-xl); /* section → section — one step tighter, per request */
}

.tech-header {
  font-family: var(--font-mono);
  font-size: var(--caps-size);
  font-weight: var(--caps-weight);
  letter-spacing: var(--caps-tracking);
  text-transform: uppercase;
  color: var(--fg-mid);
  margin-bottom: var(--space-m); /* section label → content */
}

.tech-list {
  display: grid;
  grid-template-columns: 200px 1fr;
  row-gap: var(--space-s);
  column-gap: var(--space-m);
  max-width: 700px;
  font-family: var(--font-mono);
  font-size: 0.9375rem;
}

.tech-list dt {
  color: var(--fg-mid);
}

.tech-list dd {
  color: var(--fg);
}

@media (max-width: 640px) {
  .tech-list {
    grid-template-columns: 1fr;
    row-gap: var(--space-xs);
  }

  .tech-list dd {
    margin-bottom: var(--space-s);
  }
}

/* Section rhythm on mobile is handled by the --space-* overrides above. */
