/* Penn Purity Test — static stylesheet
   Restrained, old-university feel, modernized for phones. */

:root {
  --color-bg: #ffffff;
  --color-text: #292929;
  --color-navy: #1f3553;
  --color-red: #8a2635;
  --color-intro-gray: #585858;
  --color-muted: #666666;
  --color-divider: #dddddd;

  --font-site: "Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", Arial, sans-serif;

  --space-xs: clamp(0.25rem, 0.5vw, 0.4rem);
  --space-sm: clamp(0.5rem, 1vw, 0.75rem);
  --space-md: clamp(1rem, 2vw, 1.5rem);
  --space-lg: clamp(1.5rem, 4vw, 2.5rem);

  --page-padding: clamp(1rem, 3vw, 2rem);

  --content-width: 720px;
  --header-width: 1100px;

  /* Matches .page-heading's bottom margin so the heading-to-score gap
     and the score-to-actions gap on the result page read as equal. */
  --result-spacing: clamp(2.5rem, 5vw, 4rem);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background-color: var(--color-bg);
  -webkit-text-size-adjust: 100%;
  /* Defense-in-depth only, not load-bearing: below the mobile breakpoint,
     the open info bubble is position: fixed with its left/width computed
     directly from the visual viewport (app.js: placeMobileInfoBubble), so
     it can never contribute to the document's scrollable width in the
     first place — fixed-positioned boxes are excluded from a document's
     scrollable overflow by definition, unlike the old translate()-shifted
     absolute box. Above the breakpoint, the desktop bubble's geometric
     clamp (placeDesktopInfoBubble) keeps its rendered rect within the
     visual viewport. This rule just guards against any unforeseen
     subpixel/rounding overshoot without masking a real layout bug, since
     nothing on this page intentionally relies on horizontal overflow. */
  overflow-x: hidden;
}

body {
  margin: 0;
  border: 0;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-site);
  line-height: 1.6;
  font-size: 1rem;
}

/* Shared header + title system (identical on every page) */

.site-header {
  max-width: var(--header-width);
  margin-left: auto;
  margin-right: auto;
  padding: clamp(4rem, 8vw, 6rem) var(--page-padding) 0;
  text-align: center;
}

.page-heading {
  font-family: var(--font-site);
  font-size: clamp(3rem, 6vw, 5rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0.02em;
  color: var(--color-navy);
  text-align: center;
  margin: 0 0 clamp(2.5rem, 5vw, 4rem) 0;
}

main,
footer {
  max-width: var(--content-width);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--page-padding);
  padding-right: var(--page-padding);
}

main.question-main {
  max-width: none;
  width: 100%;
  margin-left: 0;
  margin-right: 0;
}

.intro {
  max-width: 1000px;
  margin: 0 auto clamp(2rem, 4vw, 3rem) auto;
  font-family: var(--font-site);
}

.intro p {
  color: var(--color-text);
  font-weight: 400;
  font-style: italic;
  line-height: 1.55;
  margin: 0 0 0.85em 0;
}

.intro p:last-child {
  margin-bottom: 0;
}

main {
  padding-bottom: var(--space-lg);
}

noscript p {
  color: var(--color-muted);
  border-top: 1px solid var(--color-divider);
  border-bottom: 1px solid var(--color-divider);
  padding: var(--space-sm) 0;
  margin-bottom: var(--space-lg);
}

/* Question list: semantic <ol>, visual numbering via CSS counters
   in a dedicated grid column so the number can never land inside
   the checkbox or drift to the end of a wrapped sentence. */

.question-list {
  list-style: none;
  counter-reset: question-number;
  margin: 0 0 var(--space-md) 0;
  padding: 0;
  width: 100%;
  max-width: none;
  text-align: left;
}

.question-list li {
  counter-increment: question-number;
  display: grid;
  grid-template-columns: 2.75rem minmax(0, 1fr);
  column-gap: 0.5rem;
  align-items: start;
  margin: 0 0 0.3rem 0;
}

.question-list li::before {
  content: counter(question-number) ".";
  text-align: right;
  font-size: 1.03125rem;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

.question-content {
  position: relative;
  min-width: 0;
}

.question-row {
  display: grid;
  grid-template-columns: 19px minmax(0, 1fr);
  gap: 0.65rem;
  align-items: start;
  min-height: 44px;
}

.question-checkbox {
  width: 19px;
  height: 19px;
  margin-top: 0.15rem;
  accent-color: var(--color-red);
}

.question-list label {
  min-width: 0;
  font-size: 1.03125rem;
  line-height: 1.5;
  color: var(--color-text);
  cursor: pointer;
}

.question-copy {
  min-width: 0;
  font-size: 1.03125rem;
  line-height: 1.5;
}

/* .question-info is a fixed-size, inline-flex wrapper so both it and the
   button it centers share one vertical-align: middle box — anchoring the
   circle to the text's centerline instead of drifting per the wrapper's
   own inline-block baseline. It also doubles as the positioning context
   for .info-bubble, replacing the old full-width .question-content anchor
   that pushed open notes toward the middle of the page.
   Sized to match the 19px question checkbox exactly. vertical-align: middle
   (rather than a position: relative top offset) is what lets the box
   participate correctly in the text line: it centers the box on the font's
   x-height midpoint, which at this font-size/line-height already lines the
   19px circle up with the surrounding text, so no extra pixel nudge is
   layered on top. */
.question-info {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  width: 19px;
  height: 19px;
  margin-left: 0.35rem;
}

.info-button {
  display: inline-flex;
  position: relative;
  align-items: center;
  justify-content: center;
  width: 19px;
  height: 19px;
  min-width: 0;
  min-height: 0;
  padding: 0;
  margin: 0;
  border-radius: 50%;
  border: 1px solid var(--color-navy);
  background-color: var(--color-bg);
  color: var(--color-navy);
  font-family: var(--font-site);
  font-size: 0.7rem;
  line-height: 1;
  cursor: pointer;
}

.info-button::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}

.info-bubble {
  position: absolute;
  z-index: 20;
  width: max-content;
  /* Fallback sizing only (matches the JS-computed inline max-width applied
     the instant a bubble opens). Kept in sync with the ~10px viewport
     margin the placement script clamps against. */
  max-width: min(20rem, calc(100vw - 20px));
  padding: 0.6rem 0.75rem;
  background-color: #ffffff;
  color: var(--color-text);
  border: 1px solid #dddddd;
  border-radius: 6px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
  font-family: var(--font-site);
  font-size: 0.875rem;
  font-style: normal;
  font-weight: 400;
  line-height: 1.4;
  text-align: left;
  overflow-wrap: break-word;
}

.info-bubble[hidden] {
  display: none;
}

/* Every placement variant below resolves to a final on-screen position via
   translate(), combining the CSS-declared base offset with a JS-computed
   --bubble-shift-x/-y correction (app.js: placeInfoBubble). The shift is
   calculated from getBoundingClientRect() in the same screen-space pixel
   units that translate() consumes, so no coordinate conversion is needed —
   it's a direct, always-in-bounds clamp regardless of which side is chosen. */

/* Default: directly right of the button, vertically centered on it. */
.info-bubble--right {
  left: calc(100% + 0.55rem);
  top: 50%;
  transform: translate(var(--bubble-shift-x, 0px), calc(-50% + var(--bubble-shift-y, 0px)));
}

/* Fallback near the right viewport edge: open to the left instead. */
.info-bubble--left {
  left: auto;
  right: calc(100% + 0.55rem);
  top: 50%;
  transform: translate(var(--bubble-shift-x, 0px), calc(-50% + var(--bubble-shift-y, 0px)));
}

/* Narrow-screen fallback: open below the button, extending toward
   whichever side keeps it inside the viewport. */
.info-bubble--below-start {
  top: calc(100% + 0.4rem);
  left: 0;
  transform: translate(var(--bubble-shift-x, 0px), var(--bubble-shift-y, 0px));
}

.info-bubble--below-end {
  top: calc(100% + 0.4rem);
  left: auto;
  right: 0;
  transform: translate(var(--bubble-shift-x, 0px), var(--bubble-shift-y, 0px));
}

/* Fallback for when there isn't room below either (near the bottom of the
   list): open above the button instead. */
.info-bubble--above-start {
  top: auto;
  bottom: calc(100% + 0.4rem);
  left: 0;
  transform: translate(var(--bubble-shift-x, 0px), var(--bubble-shift-y, 0px));
}

.info-bubble--above-end {
  top: auto;
  bottom: calc(100% + 0.4rem);
  left: auto;
  right: 0;
  transform: translate(var(--bubble-shift-x, 0px), var(--bubble-shift-y, 0px));
}

.info-bubble::after {
  content: "";
  position: absolute;
  width: 0;
  height: 0;
  border: 6px solid transparent;
}

/* --caret-offset-x/-y (app.js: positionBubbleCaret) is the caret's final
   top-left edge position in bubble-local px, computed from the button's
   actual center *after* the bubble's own viewport clamp is applied. That
   keeps the arrow pointed at the button even when a large clamp shift has
   moved the bubble far from its unclamped default position. The literal
   fallback values below (matching the old fixed offsets) only apply if JS
   never ran, e.g. bubble.style failed to set the property. */

.info-bubble--right::after {
  left: -6px;
  top: var(--caret-offset-y, calc(50% - 6px));
  border-right-color: #ffffff;
}

.info-bubble--left::after {
  right: -6px;
  top: var(--caret-offset-y, calc(50% - 6px));
  border-left-color: #ffffff;
}

.info-bubble--below-start::after {
  top: -6px;
  left: var(--caret-offset-x, 12px);
  border-bottom-color: #ffffff;
}

.info-bubble--below-end::after {
  top: -6px;
  left: var(--caret-offset-x, calc(100% - 24px));
  right: auto;
  border-bottom-color: #ffffff;
}

.info-bubble--above-start::after {
  top: auto;
  bottom: -6px;
  left: var(--caret-offset-x, 12px);
  border-top-color: #ffffff;
}

.info-bubble--above-end::after {
  top: auto;
  bottom: -6px;
  left: var(--caret-offset-x, calc(100% - 24px));
  right: auto;
  border-top-color: #ffffff;
}

/* Mobile/narrow layout: the open bubble becomes a viewport-fixed panel
   instead of a box anchored to the button's 19px wrapper. This rule is
   deliberately *not* gated behind an @media breakpoint: CSS media queries
   evaluate the layout viewport, while app.js's mobile-mode decision also
   considers touch/coarse-pointer devices and the visual viewport (which
   can differ from the layout viewport in landscape or while pinch-zoomed).
   Gating position: fixed behind a width breakpoint here could let JS
   compute fixed-panel geometry while CSS left the bubble position:
   absolute. Instead, applying .info-bubble--mobile is itself the single
   source of truth for fixed positioning, so JS and CSS can never disagree.
   left/width/top (and max-height+overflow-y, only when content is
   genuinely taller than the viewport) are computed and applied inline by
   placeMobileInfoBubble(); the rules here establish position: fixed and
   explicitly reset every property the desktop variants use, so nothing
   from a previous desktop placement can carry over. */
.info-bubble--mobile {
  position: fixed;
  top: 0;
  left: 0;
  right: auto;
  bottom: auto;
  width: auto;
  max-width: none;
  transform: none;
}

.info-bubble--mobile::after {
  display: none;
}

.info-bubble--mobile-below::after {
  display: block;
  top: -6px;
  left: var(--caret-offset-x, 12px);
  border-bottom-color: #ffffff;
}

.info-bubble--mobile-above::after {
  display: block;
  top: auto;
  bottom: -6px;
  left: var(--caret-offset-x, 12px);
  border-top-color: #ffffff;
}

.controls {
  text-align: center;
  padding-top: var(--space-sm);
}

#selected-count {
  color: var(--color-muted);
  margin: 0 0 var(--space-sm) 0;
}

button,
.button-link {
  display: inline-block;
  font-family: var(--font-site);
  font-size: 1rem;
  min-height: 44px;
  min-width: 44px;
  padding: 0.65em 1.25em;
  margin: var(--space-xs);
  border-radius: 4px;
  border: 1px solid var(--color-navy);
  background-color: var(--color-bg);
  color: var(--color-navy);
  cursor: pointer;
  text-align: center;
}

#calculate-button,
#share-button {
  background-color: var(--color-navy);
  color: var(--color-bg);
  border-color: var(--color-navy);
}

#clear-button {
  border-color: var(--color-red);
  color: var(--color-red);
}

.error-message {
  color: var(--color-red);
  text-align: center;
  min-height: 1.5em;
}

.error-message:empty {
  display: none;
}

footer {
  text-align: center;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: var(--space-md);
  padding-bottom: 2.5rem;
}

footer a {
  color: var(--color-muted);
  font-size: 0.9rem;
}

a {
  color: var(--color-red);
  text-decoration: underline;
}

a:visited {
  color: var(--color-red);
}

/* .button-link is an <a> styled as a control; it must keep its button
   coloring even once visited, rather than falling back to link red. */
a.button-link,
a.button-link:visited,
a.button-link:hover,
a.button-link:focus-visible,
a.button-link:active {
  color: var(--color-navy);
  text-decoration: none;
}

/* Result page specifics */

.score-display {
  font-family: var(--font-site);
  font-size: clamp(5.5rem, 13vw, 7.5rem);
  line-height: 0.95;
  font-weight: 400;
  color: var(--color-text);
  text-align: center;
  margin: 0 0 var(--result-spacing) 0;
}

/* The score's own margin already supplies the full result-page gap, so
   the shared .controls padding-top (meant for the homepage's controls)
   is zeroed here to avoid stacking a second, unequal gap. */
#result-available .controls {
  padding-top: 0;
}

.status-region {
  text-align: center;
  color: var(--color-muted);
  min-height: 1.5em;
}

/* About page sections */

.about-main {
  text-align: left;
}

section {
  margin-bottom: var(--space-lg);
}

section h2 {
  font-family: var(--font-site);
  color: var(--color-navy);
  font-weight: normal;
  letter-spacing: 0.02em;
  margin: 0 0 0.6em 0;
}

.about-main p {
  line-height: 1.7;
  margin: 0 0 1em 0;
}

.page-links {
  margin-top: var(--space-md);
}

.page-links a {
  margin-right: 1rem;
}

/* Compact centered compositions (404, unavailable result) */

.center-main {
  text-align: center;
}

.hidden {
  display: none;
}

/* Strong visible focus styles */

a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 3px solid var(--color-red);
  outline-offset: 2px;
}

/* Reduced motion */

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

@media print {
  .controls,
  footer {
    display: none;
  }
}
