/* Shared design tokens — see .claude/skills/accessible-minimal-design.
   Page-specific stylesheets should reuse these vars, not redefine them. */
:root{
  --bg:#FBFBFA;
  --surface:#FFFFFF;
  --ink:#1A1A1A;
  --muted:#5B5B5B;
  --line:#EAEAEA;
  --accent:#2F4858;
  --accent-ink:#FFFFFF;
}
/* Dark mode: same soft, muted approach — not pure black/white, same single
   accent (lightened for contrast on a dark background), toggled via
   assets/js/theme.js. */
:root[data-theme="dark"]{
  --bg:#15181B;
  --surface:#1D2125;
  --ink:#ECECEA;
  --muted:#A3A9AE;
  --line:#2C3135;
  --accent:#7FA8BE;
  --accent-ink:#15181B;
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
/* Safety net: no single element should be able to widen the page past the
   viewport and push the right-side margin off-screen — if one slips
   through (a long unbroken string, a fixed-width embed), clip at the
   document edge instead of letting the whole page scroll horizontally. */
html,body{overflow-x:hidden}
body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;line-height:1.65;color:var(--ink);background:var(--bg)}
a{color:var(--accent)}
/* Functional icon, not decoration: marks any link that opens in a new tab,
   applied once here so every such link site-wide gets it automatically. */
a[target="_blank"]::after{
  content:"";display:inline-block;width:.7em;height:.7em;margin-left:.45em;vertical-align:-0.05em;
  background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14 3h7v7h-2V6.4l-9.3 9.3-1.4-1.4L17.6 5H14V3Z'/%3E%3Cpath d='M5 5h6v2H7v10h10v-4h2v6H5V5Z'/%3E%3C/svg%3E") no-repeat center / contain;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14 3h7v7h-2V6.4l-9.3 9.3-1.4-1.4L17.6 5H14V3Z'/%3E%3Cpath d='M5 5h6v2H7v10h10v-4h2v6H5V5Z'/%3E%3C/svg%3E") no-repeat center / contain;
}

a,button,input,select,textarea{transition:background-color .2s ease,border-color .2s ease,color .2s ease,box-shadow .2s ease,transform .1s ease}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{
  outline:none;box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);border-radius:4px;
}
/* Feedback on press, not just hover/release — a button should feel caught
   the instant it's touched. Scoped to buttons/pill-links only, not every
   link or input, so it stays a deliberate tactile cue rather than noise. */
@media (prefers-reduced-motion: no-preference){
  button:active,.btn:active,.btn-primary:active,.btn-hero:active,.icon-btn:active{transform:scale(.97)}
}

/* Fixed height (not padding-driven) so this bar is pixel-identical on every
   page regardless of nav content (guest vs signed-in) or font rendering. */
.site-header{display:flex;justify-content:space-between;align-items:center;height:64px;padding:0 28px;background:var(--surface);color:var(--ink);border-bottom:1px solid var(--line)}
.site-header .brand{display:inline-flex;align-items:center;gap:8px;color:var(--ink);font-weight:600;text-decoration:none;font-size:1.1rem;letter-spacing:.02em;flex-shrink:0}
.brand-logo{height:28px;display:block}
/* Shown until a real logo is uploaded (public/admin/config.php) or anywhere
   else render_logo_mark() is used without one set — a plain initials mark,
   deliberately not trying to look like a finished brand. */
.logo-placeholder{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:7px;background:var(--accent);color:var(--accent-ink);font-weight:700;font-size:.74rem;letter-spacing:.02em;flex-shrink:0}
.site-nav{display:flex;gap:20px;align-items:center;flex-wrap:wrap;justify-content:flex-end;min-width:0}
.site-nav a{color:var(--muted);text-decoration:none;font-weight:500;font-size:.92rem}
.site-nav a:hover{color:var(--ink)}
.site-nav .who{color:var(--muted);font-size:.88rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:40vw}
@media(max-width:480px){.site-header{height:auto;min-height:64px;padding:12px 16px;flex-wrap:wrap;gap:8px}.site-nav{gap:14px}}

/* Homepage-only: the nav floats, transparent, over the full-bleed hero so
   its own dot-pattern background shows through the bar instead of the
   nav having a surface of its own. Taken out of flow (absolute) so the
   hero section can start at the very top of the page, behind it. The hero
   is a light grey, so nav text/icons stay dark here too — same contrast
   as the default header, just without the surface fill behind it. */
.site-header--overlay{position:absolute;top:0;left:0;right:0;z-index:20;background:transparent;border-bottom:none;color:#1A1A1A}
.site-header--overlay .brand{color:#1A1A1A}
.site-header--overlay .site-nav a{color:#4A4A4A}
.site-header--overlay .site-nav a:hover{color:#1A1A1A}
.site-header--overlay .site-nav .who{color:#5B5B5B}
.site-header--overlay .icon-btn:hover{background:rgba(0,0,0,.06)}
/* Dark mode: the hero underneath flips to a dark wash (see .site-hero
   below), so this nav's text has to flip from dark-on-light to
   light-on-dark too, or it goes illegible against its own background. */
:root[data-theme="dark"] .site-header--overlay{color:#ECECEA}
:root[data-theme="dark"] .site-header--overlay .brand{color:#ECECEA}
:root[data-theme="dark"] .site-header--overlay .site-nav a{color:#C7CBCE}
:root[data-theme="dark"] .site-header--overlay .site-nav a:hover{color:#ECECEA}
:root[data-theme="dark"] .site-header--overlay .site-nav .who{color:#A3A9AE}
:root[data-theme="dark"] .site-header--overlay .icon-btn:hover{background:rgba(255,255,255,.1)}

/* Generic small icon-only button: square, centered icon, subtle hover —
   used for the theme toggle and the sidebar toggle. Icons inherit
   currentColor so they adapt to whatever surface they sit on. */
.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:8px;background:transparent;color:inherit;cursor:pointer}
.icon-btn:hover{background:var(--bg)}
.icon-btn svg{display:block}
.theme-toggle .icon-moon{display:none}
:root[data-theme="dark"] .theme-toggle .icon-sun{display:none}
:root[data-theme="dark"] .theme-toggle .icon-moon{display:block}

.site-footer{text-align:center;color:var(--muted);font-size:.84rem;padding:32px 20px;line-height:1.6}

/* Shared full-bleed hero, used on the homepage and every subject page
   (study guide, quiz) — one consistent background/overlay-nav treatment
   site-wide rather than a one-off per page. Spans the entire viewport
   width edge-to-edge and starts at y=0, running behind the transparent
   overlay nav (.site-header--overlay, opt in via $navOverlay) rather
   than below it. Extra top padding on the inner wrapper reserves room
   for that floating nav. */
.site-hero{
  position:relative;width:100%;margin:0;overflow:hidden;color:#1A1A1A;
  /* A soft, light grey wash rather than a flat fill — reads as quiet and
     airy instead of a loud color block, and keeps nav/body text in the
     same dark-on-light register used everywhere else on the site. A dark
     wash of the same idea is swapped in under dark mode just below,
     rather than forcing this exact light fill on both themes. */
  background-color:#EEF0F1;
  /* A hand-placed scatter of short line segments at uneven angles,
     lengths and positions (an SVG tile, not repeating-linear-gradient) —
     repeating-linear-gradient lines always cross at one consistent angle
     pair, which reads as a grid of squares/diamonds no matter the
     spacing. Irregular placement is what actually looks abstract/random. */
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220' viewBox='0 0 220 220'%3E%3Cg stroke='black' stroke-width='1' fill='none'%3E%3Cline x1='12' y1='24' x2='58' y2='4' stroke-opacity='.05'/%3E%3Cline x1='146' y1='18' x2='201' y2='63' stroke-opacity='.045'/%3E%3Cline x1='24' y1='96' x2='88' y2='142' stroke-opacity='.05'/%3E%3Cline x1='104' y1='8' x2='126' y2='66' stroke-opacity='.04'/%3E%3Cline x1='162' y1='112' x2='108' y2='176' stroke-opacity='.05'/%3E%3Cline x1='34' y1='156' x2='76' y2='206' stroke-opacity='.045'/%3E%3Cline x1='182' y1='146' x2='146' y2='198' stroke-opacity='.04'/%3E%3Cline x1='4' y1='118' x2='48' y2='58' stroke-opacity='.05'/%3E%3Cline x1='210' y1='4' x2='172' y2='52' stroke-opacity='.045'/%3E%3Cline x1='72' y1='32' x2='118' y2='96' stroke-opacity='.04'/%3E%3Cline x1='2' y1='188' x2='42' y2='152' stroke-opacity='.05'/%3E%3Cline x1='128' y1='172' x2='194' y2='204' stroke-opacity='.045'/%3E%3Cline x1='66' y1='78' x2='18' y2='34' stroke-opacity='.04'/%3E%3Cline x1='176' y1='84' x2='216' y2='136' stroke-opacity='.05'/%3E%3Cline x1='94' y1='128' x2='62' y2='172' stroke-opacity='.04'/%3E%3Cline x1='138' y1='56' x2='158' y2='10' stroke-opacity='.045'/%3E%3C/g%3E%3C/svg%3E"),
    linear-gradient(to bottom, #E8EBEC, #F4F5F6);
  background-size:220px 220px, 100% 100%;
  background-repeat:repeat, no-repeat;
}
/* Dark mode: same idea (soft wash + scattered-line texture), just dark
   instead of light, so the hero doesn't sit as a jarring light-grey
   block when the rest of the site is dark. Same SVG tile with white
   strokes instead of black, since the previous one would be invisible
   against a dark fill. */
:root[data-theme="dark"] .site-hero{
  color:#ECECEA;
  background-color:#1A1D20;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220' viewBox='0 0 220 220'%3E%3Cg stroke='white' stroke-width='1' fill='none'%3E%3Cline x1='12' y1='24' x2='58' y2='4' stroke-opacity='.06'/%3E%3Cline x1='146' y1='18' x2='201' y2='63' stroke-opacity='.055'/%3E%3Cline x1='24' y1='96' x2='88' y2='142' stroke-opacity='.06'/%3E%3Cline x1='104' y1='8' x2='126' y2='66' stroke-opacity='.05'/%3E%3Cline x1='162' y1='112' x2='108' y2='176' stroke-opacity='.06'/%3E%3Cline x1='34' y1='156' x2='76' y2='206' stroke-opacity='.055'/%3E%3Cline x1='182' y1='146' x2='146' y2='198' stroke-opacity='.05'/%3E%3Cline x1='4' y1='118' x2='48' y2='58' stroke-opacity='.06'/%3E%3Cline x1='210' y1='4' x2='172' y2='52' stroke-opacity='.055'/%3E%3Cline x1='72' y1='32' x2='118' y2='96' stroke-opacity='.05'/%3E%3Cline x1='2' y1='188' x2='42' y2='152' stroke-opacity='.06'/%3E%3Cline x1='128' y1='172' x2='194' y2='204' stroke-opacity='.055'/%3E%3Cline x1='66' y1='78' x2='18' y2='34' stroke-opacity='.05'/%3E%3Cline x1='176' y1='84' x2='216' y2='136' stroke-opacity='.06'/%3E%3Cline x1='94' y1='128' x2='62' y2='172' stroke-opacity='.05'/%3E%3Cline x1='138' y1='56' x2='158' y2='10' stroke-opacity='.055'/%3E%3C/g%3E%3C/svg%3E"),
    linear-gradient(to bottom, #1D2125, #15181B);
  background-size:220px 220px, 100% 100%;
  background-repeat:repeat, no-repeat;
}
/* Smoothly dissolves the hero's grey background into the page background
   right where it meets the feature row below — tall and gradual so there's
   no visible seam, instead of a hard cut from one fill to the other. */
.site-hero-fade{position:absolute;left:0;right:0;bottom:0;height:280px;background:linear-gradient(to bottom, transparent, var(--bg));pointer-events:none}
/* The study guide/quiz heroes carry less content (no photo, one short
   line of copy) than the homepage's, so a smaller fade keeps it in
   proportion to that shorter box instead of visually eating most of it. */
.site-hero--compact .site-hero-fade{height:130px}
/* The compact hero (study guide/quiz pages) carries far less content than
   the homepage's full hero, so it doesn't need that hero's same large
   bottom padding — just the top padding needed to clear the floating nav. */
.site-hero--compact .site-hero-inner{padding-bottom:48px}
.site-hero-inner{
  /* Equal top/bottom keeps the content block vertically centered in the
     hero. clamp() scales it with viewport width instead of a fixed px
     value, so the floating nav gets generous breathing room on a tall
     desktop viewport without leaving a big dead gap on a short mobile
     one — same rule, same centering, on every page that uses this hero. */
  position:relative;max-width:1400px;margin:0 auto;
  padding:clamp(120px, 22vw, 190px) 48px clamp(60px, 10vw, 100px);
  display:flex;align-items:center;justify-content:space-between;gap:48px;
}
.site-hero-text{max-width:560px}
.site-hero .eyebrow{text-transform:uppercase;letter-spacing:.09em;font-weight:600;font-size:.77rem;color:var(--accent)}
/* Large display text wants tighter leading and slightly negative tracking
   as it grows — a fixed letter-spacing reads wrong at this size. */
.site-hero h1{font-size:clamp(1.8rem,4vw,2.4rem);font-weight:600;margin:.4rem 0 .7rem;line-height:1.08;letter-spacing:-0.015em}
.site-hero p{color:#4A4A4A;line-height:1.65}
:root[data-theme="dark"] .site-hero p{color:#C7CBCE}
.site-hero-photo{flex:0 0 auto;width:min(420px,42vw);height:auto;border-radius:20px;box-shadow:0 30px 70px -24px rgba(0,0,0,.22)}
/* Smaller square crop for hero cards where the photo is a modest supporting
   element next to the text, not the dominant visual (e.g. the study guide
   page) — same material (radius + shadow), just a compact footprint. */
.site-hero-photo--square{width:min(200px,30vw);height:min(200px,30vw);object-fit:cover}
/* A wider crop (matches the source photo's own 3:2 ratio) for hero cards
   where a square would cut off too much of the sides of the shot. */
.site-hero-photo--wide{width:min(300px,36vw);aspect-ratio:3/2;height:auto;object-fit:cover}
.site-hero-cta{margin-top:22px}
.btn-hero{display:inline-block;background:var(--accent);color:var(--accent-ink);font-weight:600;padding:13px 22px;border-radius:999px;text-decoration:none;font-size:.95rem}
.btn-hero:hover{background:color-mix(in srgb, var(--accent) 88%, black)}
@media(max-width:760px){
  /* Vertical padding stays on the clamp() from .site-hero-inner — it
     already scales down for narrow viewports on its own. Only the
     layout (stacked, centered) and horizontal gutter change here. */
  .site-hero-inner{flex-direction:column;text-align:center;padding-left:28px;padding-right:28px}
  .site-hero-text{max-width:none}
  .site-hero-photo{width:min(320px,80vw)}
  .site-hero-photo--square{width:min(160px,50vw);height:min(160px,50vw)}
  .site-hero-photo--wide{width:min(280px,78vw)}
}

/* Marketing-style feature row between the hero and the subject cards —
   icon-sized graphics (not a full illustration), so they support the
   heading/copy instead of dominating it. Fixed 3-up grid (not auto-fit)
   so it doesn't reflow unpredictably at in-between widths; single
   column on mobile only. */
.feature-row{max-width:1400px;margin:44px auto;padding:0 24px;display:grid;gap:28px;grid-template-columns:repeat(3,1fr)}
.feature{text-align:center}
.feature-graphic{width:28px;height:28px;color:var(--accent);margin-bottom:10px}
@media(max-width:700px){.feature-row{grid-template-columns:1fr;max-width:420px}}
.feature h3{font-size:1.02rem;font-weight:600;margin:0 0 8px;letter-spacing:-0.01em}
.feature p{color:var(--muted);font-size:.9rem;line-height:1.6;margin:0}

/* Scroll-reveal: a cascading fade-up as each item enters the viewport,
   wired up by assets/js/reveal.js (IntersectionObserver). Respects
   prefers-reduced-motion — reveal.js skips the motion and just shows
   everything immediately when the user has asked for less motion. */
.reveal{opacity:0;transform:translateY(18px);transition:opacity .6s cubic-bezier(.16,1,.3,1),transform .6s cubic-bezier(.16,1,.3,1);transition-delay:calc(var(--d, 0) * 90ms)}
.reveal.is-visible{opacity:1;transform:none}
@media (prefers-reduced-motion: reduce){
  .reveal{opacity:1;transform:none;transition:none}
}

.subject-grid-heading{max-width:1400px;margin:90px auto 0;padding:0 48px;font-size:1.3rem;font-weight:600;text-align:left}
.subject-grid{max-width:1400px;margin:20px auto 40px;padding:0 24px;display:grid;gap:24px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
/* A subtle lift on hover — the card reads as a real raised surface you
   could pick up, not a flat rectangle. Transform/opacity only, so it's
   compositor-friendly and stays smooth. */
.subject-card{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:26px;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s cubic-bezier(.16,1,.3,1)}
/* A card that also carries a supporting photo (currently just Part 107) —
   text on the left, a square photo on the right, instead of the plain
   text-only card every other subject uses. */
.subject-card--photo{display:flex;align-items:center;justify-content:space-between;gap:20px}
.subject-card-body{flex:1;min-width:0}
.subject-card-head{display:flex;align-items:center;gap:12px;margin-bottom:10px}
.subject-card-icon{width:32px;height:32px;color:var(--accent);flex:0 0 auto}
.subject-card-head h2{margin:0;line-height:1}
.subject-card-photo{flex:0 0 auto;width:110px;height:110px;border-radius:14px;object-fit:cover}
.subject-card:hover{transform:translateY(-4px);box-shadow:0 16px 32px -20px rgba(0,0,0,.25)}
.subject-card h2{margin:0 0 10px;font-size:1.15rem;font-weight:600;letter-spacing:-0.01em}
.subject-card p{color:var(--muted);font-size:.92rem;line-height:1.6}
.subject-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:16px}
@media(max-width:560px){.subject-card--photo{flex-direction:column;text-align:center}.subject-card-head{flex-direction:column}}

.btn{display:inline-block;padding:10px 18px;border-radius:8px;text-decoration:none;font-weight:500;font-size:.9rem;border:1px solid var(--line);color:var(--ink);background:var(--surface)}
.btn:hover{background:var(--bg)}
.btn-primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.btn-primary:hover{background:color-mix(in srgb, var(--accent) 88%, black)}
