/* Self-hosted so no font request ever leaves the browser. Only 400 and
   600 exist -- the only weights used anywhere on the site. Same files
   Haven uses (IBM Plex Mono is open-licensed, redistribution is fine). */
@font-face{
  font-family:'IBM Plex Mono';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('fonts/ibmplexmono-400.woff2') format('woff2');
}
@font-face{
  font-family:'IBM Plex Mono';
  font-style:normal;
  font-weight:600;
  font-display:swap;
  src:url('fonts/ibmplexmono-600.woff2') format('woff2');
}

:root{
  --bg:#FAFCF7;
  --text-color:#101312;
  --text-muted:#4B5650;
  --border-color:#111513;
  --border-color-rgb:17,21,19;
  --border-default:2px solid var(--border-color);
  --panel-bg:#FFFFFF;
  /* The one accent hue used everywhere -- alligator green. Soft/vivid two-state
     like a button rest/hover pair, plus a darker text-safe shade for anything
     sitting on a light-green background (badges, active states). */
  --accent:#3FAE5C;
  --accent-rgb:63,174,92;
  --accent-soft:#DCF3E1;
  --accent-vivid:#2E8C48;
  --accent-text:#1D5C31;
  /* --accent itself only measures 2.83:1 against white -- well under WCAG
     AA's 4.5:1 for text (or even the 3:1 non-text-contrast minimum for an
     icon like .cat-icon's). Anywhere white text or a white icon sits on a
     solid green fill (primary buttons, active mode-btn, category icons,
     the checked checkbox) uses this darker shade instead, which clears
     4.5:1 (5.11:1, verified). --accent itself stays the lighter "light
     green" brand color everywhere else -- borders, the mascot, badges with
     dark text on top -- where no contrast problem exists. */
  --accent-strong:#2A7D40;
  --radius:10px;
  --shadow-pop:4px 4px 0 rgba(var(--border-color-rgb),1);
  --shadow-pop-sm:2px 2px 0 rgba(var(--border-color-rgb),1);
  --shadow-pop-accent:4px 4px 0 var(--accent);
  --font-mono:'IBM Plex Mono',monospace;
  --font-size-base:0.9375rem;
  --font-size-small:0.875rem;
  --line-height:1.5;
  --content-width:1100px;
  --transition-hover:transform .1s ease, box-shadow .1s ease;
  --hover-lift:translate(-2px,-2px);
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
/* Off-screen until keyboard-focused (shell.js injects this as the page's
   first focusable element on every page) -- standard skip-link pattern,
   not visible to sighted mouse users but the first thing a keyboard/screen
   reader user encounters. */
.skip-link{
  position:absolute;
  left:8px;top:-60px;
  background:var(--accent-strong);
  color:#fff;
  padding:10px 16px;
  border-radius:var(--radius);
  border:var(--border-default);
  font-family:var(--font-mono);
  font-weight:600;
  font-size:var(--font-size-small);
  text-decoration:none;
  z-index:1000;
  transition:top .15s ease;
}
.skip-link:focus{top:8px;}
@media (prefers-reduced-motion: reduce){
  .skip-link{transition:none;}
}
main:focus-visible{outline:none;}
body{
  /* Faint graph-paper grid -- a math/calculator motif instead of Haven's
     gradient-blob background, fixed so it doesn't scroll with content. */
  background-image:
    linear-gradient(rgba(var(--accent-rgb),0.08) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--accent-rgb),0.08) 1px, transparent 1px);
  background-size:28px 28px;
  background-attachment:fixed;
  background-color:var(--bg);
  color:var(--text-color);
  font-family:var(--font-mono);
  font-weight:400;
  line-height:var(--line-height);
  min-height:100vh;
  /* Display-only lowercase look, matching Haven/ydnim.com's own brand voice --
     the underlying markup everywhere still uses normal English capitalization
     (accessibility/SEO/screen-reader correctness is unaffected), only the
     visual rendering is lowercase. */
  text-transform:lowercase;
}
::selection{background:var(--accent-strong);color:#fff;}
::-moz-selection{background:var(--accent-strong);color:#fff;}
a{color:var(--accent-text);text-decoration:underline;text-decoration-color:rgba(var(--accent-rgb),0.5);text-underline-offset:3px;}
a:hover, a:focus-visible{text-decoration-color:var(--accent-text);}
/* Form controls don't inherit text-transform by default, so without this
   they'd ignore the lowercase rule above (buttons, selects, GPA's grade
   dropdown, etc. would render in their typed-in case instead). */
button, select, input{font-family:inherit;text-transform:inherit;}
/* The lowercase look is for the site's OWN copy -- a typed value in these two
   fields is the user's actual content (an essay being word-counted, a real
   course name), so it stays exactly as typed instead of silently rendering
   different from what the user wrote. Placeholder text is still our own
   copy, so it keeps the lowercase look (set below, after this reset). */
#textInput, .course-name{text-transform:none;}
#textInput::placeholder, .course-name::placeholder{text-transform:lowercase;}

a:focus-visible,
.btn:focus-visible,
.tool-card:focus-visible,
.mode-btn:focus-visible,
.field select:focus-visible,
.field input:focus-visible{
  outline:3px solid var(--accent-vivid);
  outline-offset:2px;
}

/* ---------- Header ---------- */
header.shell-header{
  background:var(--panel-bg);
  border-bottom:var(--border-default);
  position:sticky;
  top:0;
  z-index:10;
  padding:16px 32px;
  display:flex;
  align-items:center;
  justify-content:space-between;
  flex-wrap:wrap;
  gap:12px;
}
/* Zigzag "teeth" divider -- the site's signature motif, an alligator-jaw
   callback replacing the smooth wavy line a different site (Haven) uses.
   Tiled SVG triangle wave; color is hardcoded since custom properties
   can't be read inside a data-URI SVG. */
header.shell-header::after{
  content:'';
  position:absolute;
  left:0;
  right:0;
  top:100%;
  height:10px;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='10' viewBox='0 0 20 10'%3E%3Cpath d='M0 0 L10 10 L20 0 Z' fill='%23111513'/%3E%3C/svg%3E") top / 20px 10px repeat-x;
  pointer-events:none;
}
header.shell-header{position:relative;}
.wordmark{display:flex;align-items:center;gap:10px;}
.wordmark a{display:flex;align-items:center;gap:10px;color:inherit;text-decoration:none;}
.wordmark .mascot{display:block;width:38px;height:38px;flex-shrink:0;object-fit:contain;}
.wordmark .site-name{
  font-family:var(--font-mono);
  font-weight:600;
  font-size:1.2rem;
  color:var(--text-color);
}
.wordmark .site-name .accent-letters{color:var(--accent-vivid);}
.wordmark .tagline{
  font-family:var(--font-mono);
  font-size:var(--font-size-small);
  font-weight:400;
  color:var(--text-muted);
  padding-left:10px;
  border-left:var(--border-default);
}
@media (max-width:800px){
  .wordmark .tagline{display:none;}
}
.header-links{display:flex;gap:16px;font-size:var(--font-size-small);}

/* ---------- Layout ---------- */
main{
  max-width:var(--content-width);
  margin:0 auto;
  padding:36px 24px 0;
}
main:not(.tool-main) > * + *{margin-top:36px;}
/* Grid, not flex -- breadcrumb and the title block are full-width rows
   (.full-width, grid-column:1/-1) above the two real columns, so
   tool-primary and tool-side both start on the same grid row and align
   at the same top edge. Letting the title block live only inside
   tool-primary (an earlier version) made the sidebar's result panel
   float noticeably higher than the input panel beside it -- a real
   layout bug, not just a spacing preference. One shared `gap` handles
   every row AND column gap, so there's no separate margin value to keep
   in sync with it. */
main.tool-main{
  display:grid;
  grid-template-columns:1fr 320px;
  gap:28px;
  align-items:start;
}
main.tool-main .full-width{grid-column:1 / -1;}
main.tool-main .tool-primary{display:flex;flex-direction:column;gap:20px;min-width:0;}
/* Sticky, not just align-items:start -- when the left column (inputs +
   "How this works") runs much longer than the right column (result +
   related tools), a plain static sidebar stalls near the top of a tall
   grid row and leaves a large dead gap of page background below it before
   the next section starts. Sticking it under the header instead means it
   travels down the page with the user's scroll position, so there's
   always something next to whatever part of the left column they're
   reading, and no empty gap reads as "missing content." top matches the
   header's own height (~74px) plus its zigzag edge (10px) plus a small
   buffer. Turned off below 820px, where the columns stack and stickiness
   would just pin the sidebar oddly above the (now full-width) primary
   column. */
main.tool-main .tool-side{display:flex;flex-direction:column;gap:20px;min-width:0;position:sticky;top:96px;align-self:start;}
@media (max-width:820px){
  main.tool-main{grid-template-columns:1fr;}
  main.tool-main .tool-side{position:static;}
}

.breadcrumb{
  font-size:var(--font-size-small);
  color:var(--text-muted);
  display:flex;
  gap:6px;
  flex-wrap:wrap;
}
.breadcrumb a{color:var(--text-muted);}
.breadcrumb .sep{color:var(--text-muted);}
.breadcrumb .current{color:var(--text-color);}

.tool-title{
  font-family:var(--font-mono);
  font-weight:600;
  font-size:1.4rem;
  color:var(--text-color);
  margin:0;
}
.tool-subtitle{
  font-size:var(--font-size-base);
  color:var(--text-muted);
  margin:6px 0 0;
}

/* ---------- Panels ---------- */
.panel{
  background:var(--panel-bg);
  border:var(--border-default);
  border-radius:var(--radius);
  box-shadow:var(--shadow-pop);
  padding:22px;
}
fieldset{border:none;margin:0;padding:0;min-width:0;}
.panel h2, .panel legend{
  font-family:var(--font-mono);
  font-weight:600;
  font-size:var(--font-size-base);
  color:var(--text-color);
  margin:0 0 4px;
  padding:0;
}
.field-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
@media (max-width:520px){.field-grid{grid-template-columns:1fr;}}
.field{display:flex;flex-direction:column;gap:6px;margin-top:16px;}
.field:first-child{margin-top:0;}
.field-grid .field{margin-top:0;}
.field label{
  font-family:var(--font-mono);
  font-size:var(--font-size-small);
  font-weight:600;
  color:var(--text-color);
}
.field .hint{font-size:var(--font-size-small);color:var(--text-muted);font-weight:400;}
/* .list-row's own select/text/number inputs (GPA's course rows) share this
   exact recipe -- they're not wrapped in a .field, but they're the same
   kind of boxed form control, and without this they fell back to bare
   unstyled browser defaults (including no visible focus-visible ring),
   a real gap only found once specifically auditing focus states. */
.field select,
.field input[type=number],
.field input[type=text],
.field input[type=date],
.list-row select,
.list-row input[type=text],
.list-row input[type=number]{
  background:var(--panel-bg);
  color:var(--text-color);
  border:var(--border-default);
  border-radius:var(--radius);
  padding:10px 12px;
  font-family:var(--font-mono);
  font-weight:400;
  font-size:var(--font-size-base);
  width:100%;
  min-width:0;
  transition:var(--transition-hover);
}
.field select:hover:not(:disabled), .field select:focus-visible:not(:disabled),
.field input:hover:not(:disabled), .field input:focus-visible:not(:disabled),
.list-row select:hover:not(:disabled), .list-row select:focus-visible:not(:disabled),
.list-row input:hover:not(:disabled), .list-row input:focus-visible:not(:disabled){
  border-color:var(--accent-vivid);
  transform:var(--hover-lift);
  box-shadow:var(--shadow-pop-accent);
}
.field select:disabled, .field input:disabled{opacity:0.45;cursor:not-allowed;}
.field input::placeholder, .list-row input::placeholder{color:var(--text-muted);opacity:0.7;}
.field-unit{position:relative;}
.field-unit input{padding-right:40px;}
.field-unit .unit-suffix{
  position:absolute;right:12px;top:50%;transform:translateY(-50%);
  font-size:var(--font-size-small);color:var(--text-muted);pointer-events:none;
}

/* 24x24, not the visually-tighter 18x18 tried first -- WCAG 2.5.8 (Target
   Size, Minimum) requires at least 24x24 CSS px for a custom control like
   this, and box-sizing:border-box (set globally above) means the width/
   height here already include the border, so this is the true click/tap
   target size, not just the visible square's inner content. */
input[type=checkbox]{
  appearance:none;
  -webkit-appearance:none;
  width:24px;
  height:24px;
  flex-shrink:0;
  margin:0;
  border:var(--border-default);
  border-radius:5px;
  background:var(--panel-bg);
  cursor:pointer;
  position:relative;
  transition:var(--transition-hover);
}
input[type=checkbox]:hover, input[type=checkbox]:focus-visible{border-color:var(--accent-vivid);transform:var(--hover-lift);}
input[type=checkbox]:checked{background:var(--accent-strong);border-color:var(--border-color);}
input[type=checkbox]:checked::after{
  content:'';
  position:absolute;
  left:7px;top:2px;
  width:5px;height:11px;
  border:solid #fff;
  border-width:0 2px 2px 0;
  transform:rotate(45deg);
}
@media (forced-colors: active){
  input[type=checkbox]:checked{background:Highlight;border-color:Highlight;}
  input[type=checkbox]:checked::after{border-color:HighlightText;}
}

.actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:20px;}
.btn{
  font-family:var(--font-mono);
  font-size:var(--font-size-base);
  font-weight:600;
  padding:11px 18px;
  border-radius:var(--radius);
  border:var(--border-default);
  background:var(--panel-bg);
  color:var(--text-color);
  cursor:pointer;
  transition:var(--transition-hover);
}
.btn:hover:not(:disabled), .btn:focus-visible:not(:disabled){
  transform:var(--hover-lift);
  box-shadow:var(--shadow-pop);
}
.btn:disabled{opacity:0.45;cursor:not-allowed;}
.btn.primary{background:var(--accent-strong);color:#fff;border-color:var(--border-color);}
.btn.primary:hover:not(:disabled), .btn.primary:focus-visible:not(:disabled){box-shadow:var(--shadow-pop);}
.btn.icon{padding:8px 10px;line-height:0;}
.btn.small{padding:6px 10px;font-size:var(--font-size-small);}

.mode-btn{
  display:flex;align-items:center;gap:8px;
  background:var(--panel-bg);
  border:var(--border-default);
  color:var(--text-color);
  font-family:var(--font-mono);
  font-weight:400;
  font-size:var(--font-size-small);
  padding:9px 12px;
  border-radius:var(--radius);
  cursor:pointer;
  transition:var(--transition-hover);
}
.mode-btn:hover:not(:disabled), .mode-btn:focus-visible:not(:disabled){transform:var(--hover-lift);box-shadow:var(--shadow-pop-accent);}
.mode-btn.active{background:var(--accent-strong);color:#fff;border-color:var(--border-color);}
.modes{display:flex;flex-wrap:wrap;gap:8px;}

/* ---------- Result panel ---------- */
.result-panel{
  background:var(--accent-soft);
  border:var(--border-default);
  border-radius:var(--radius);
  box-shadow:var(--shadow-pop);
  padding:22px;
}
.result-panel .result-label{
  font-size:var(--font-size-small);
  font-weight:600;
  color:var(--accent-text);
  margin:0 0 6px;
  letter-spacing:0.04em;
}
.result-panel .result-value{
  font-size:2rem;
  font-weight:600;
  color:var(--text-color);
  margin:0;
  line-height:1.2;
  word-break:break-word;
}
.result-panel .result-value.empty{color:var(--text-muted);font-size:var(--font-size-base);font-weight:400;}
.result-row{
  display:flex;justify-content:space-between;gap:10px;
  font-size:var(--font-size-small);
  padding:8px 0;
  border-top:1px solid rgba(var(--border-color-rgb),0.2);
}
.result-row:first-of-type{border-top:none;}
.result-row .label{color:var(--text-muted);}
.result-row .value{font-weight:600;color:var(--text-color);}

/* ---------- List rows (GPA course rows, merge-style file list) ---------- */
.row-list{display:flex;flex-direction:column;gap:8px;margin:10px 0 0;padding:0;list-style:none;}
.row-list:empty{margin-top:0;}
.list-row{
  display:grid;
  grid-template-columns:1fr 100px 90px 32px;
  gap:8px;
  align-items:center;
  border:var(--border-default);
  border-radius:var(--radius);
  padding:8px 10px;
  background:var(--panel-bg);
}
@media (max-width:520px){.list-row{grid-template-columns:1fr 1fr;}}
.list-row input, .list-row select{margin:0;}
/* padding:6px (not the 4px first tried) so the actual click/tap target
   clears WCAG's 24x24 CSS px minimum -- the glyph itself is much smaller,
   but padding is part of a button's hit area, not just its ink. */
.list-row .remove-row{
  background:none;border:none;cursor:pointer;color:var(--text-muted);
  font-size:1.1rem;line-height:1;padding:6px;
  border-radius:4px;
}
.list-row .remove-row:hover{color:var(--accent-vivid);}
.list-row .remove-row:focus-visible{outline:3px solid var(--accent-vivid);outline-offset:1px;}

/* ---------- Homepage / category sections ---------- */
.hero{text-align:center;padding:20px 0 0;}
.hero .mascot-hero{display:block;margin:0 auto 16px;}
.hero .mascot-hero-img{display:block;width:112px;height:112px;margin:0 auto;object-fit:contain;}
.hero h1{
  font-family:var(--font-mono);
  font-weight:600;
  font-size:1.6rem;
  margin:0 auto 14px;
  max-width:820px;
  line-height:1.35;
  color:var(--text-color);
}
.hero p{
  font-size:var(--font-size-base);
  color:var(--text-muted);
  max-width:680px;
  margin:0 auto;
}
.category-jump{
  display:flex;flex-wrap:wrap;gap:10px;justify-content:center;
}
.category-jump a{
  display:inline-flex;align-items:center;gap:6px;
  border:var(--border-default);border-radius:var(--radius);
  padding:7px 14px;font-size:var(--font-size-small);font-weight:600;
  color:var(--text-color);background:var(--panel-bg);text-decoration:none;
  transition:var(--transition-hover);
}
.category-jump a:hover, .category-jump a:focus-visible{background:var(--accent-strong);color:#fff;border-color:var(--border-color);transform:var(--hover-lift);box-shadow:var(--shadow-pop-sm);}
.category-jump svg{width:16px;height:16px;flex-shrink:0;}

/* #categorySections (the wrapper these all live in) only gets ONE 36px
   margin-top from the `main:not(.tool-main) > * + *` rule above, since
   that rule only sees direct children of <main> -- every .category-section
   inside the wrapper needs its own gap from the one before it, or they'd
   render back-to-back with zero space between them. Matches the site's
   standard 36px rhythm rather than inventing a smaller one. */
.category-section{scroll-margin-top:90px;}
.category-section + .category-section{margin-top:36px;}
.category-heading{display:flex;align-items:center;gap:10px;margin:0 0 14px;}
.category-heading .cat-icon{
  width:34px;height:34px;flex-shrink:0;
  display:flex;align-items:center;justify-content:center;
  background:var(--accent-strong);border:var(--border-default);border-radius:var(--radius);
}
.category-heading .cat-icon svg{width:19px;height:19px;color:#fff;}
.category-heading h2{
  font-family:var(--font-mono);font-weight:600;font-size:1.15rem;
  color:var(--text-color);margin:0;
}
.category-heading p{margin:0;font-size:var(--font-size-small);color:var(--text-muted);}

.tool-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;}
.tool-card{
  display:flex;flex-direction:column;gap:4px;
  border:var(--border-default);border-radius:var(--radius);
  background:var(--panel-bg);padding:16px;color:inherit;text-decoration:none;
  transition:var(--transition-hover);
}
.tool-card:hover, .tool-card:focus-visible{box-shadow:var(--shadow-pop-accent);transform:var(--hover-lift);}
.tool-card h3{font-size:var(--font-size-base);font-weight:600;margin:0;color:var(--text-color);}
.tool-card p{font-size:var(--font-size-small);color:var(--text-muted);margin:0;}

.about-section{
  padding:22px;background:var(--panel-bg);border:var(--border-default);
  border-radius:var(--radius);box-shadow:var(--shadow-pop);
}
.about-section .about-label{font-weight:600;font-size:var(--font-size-base);margin:0 0 8px;color:var(--text-color);}
.about-section p{font-size:var(--font-size-small);color:var(--text-muted);margin:0 0 10px;}
.about-section p:last-child{margin-bottom:0;}
.about-section ul{margin:0;padding-left:20px;font-size:var(--font-size-small);color:var(--text-muted);}
.about-section li{margin-bottom:6px;}

/* ---------- Related tools (same-category, scalable to hundreds of tools) ---------- */
.related-tools .related-label{
  font-size:var(--font-size-small);font-weight:600;color:var(--text-muted);
  letter-spacing:0.04em;margin:0 0 14px;
}
.related-tools .row-list{gap:8px;}
.related-tools a.related-link{
  display:flex;justify-content:space-between;gap:8px;
  border:var(--border-default);border-radius:var(--radius);
  padding:10px 12px;font-size:var(--font-size-small);color:var(--text-color);
  text-decoration:none;background:var(--panel-bg);transition:var(--transition-hover);
}
.related-tools a.related-link:hover, .related-tools a.related-link:focus-visible{box-shadow:var(--shadow-pop-sm);transform:var(--hover-lift);}
.related-tools a.related-link span.arrow{color:var(--accent-vivid);}

/* ---------- Divider + footer ---------- */
/* main's own padding-bottom is 0 (see the base `main{}` rule) --
   whatever follows </main> supplies the full top gap itself. 36px on both
   sides matches the site's one standard rhythm instead of the two
   different, smaller ad-hoc values this used before. */
.zigzag-rule{
  height:10px;width:220px;margin:36px auto;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='10' viewBox='0 0 20 10'%3E%3Cpath d='M0 0 L10 10 L20 0 Z' fill='%23111513'/%3E%3C/svg%3E");
  background-repeat:repeat-x;
}
footer.site-footer{
  max-width:var(--content-width);margin:0 auto;padding:0 24px 40px;
  text-align:center;
}
.copyright{font-size:var(--font-size-base);font-weight:600;color:var(--text-color);margin:0 0 8px;}
.footer-links{display:flex;gap:18px;justify-content:center;font-size:var(--font-size-small);}
.footer-links a{color:var(--text-muted);}
