/* Site-wide layout on top of the vendored ui-components tokens
   (tokens/light.css, tokens/dark.css) — this file only lays things out;
   colors/spacing/radii all come from the --ui-* custom properties so a
   theme change never needs a second stylesheet update. */

/* Regal palette (Sapphire Reception), layered on top of ui-components'
   own tokens via the same --ui-color-* custom properties it already
   defines — ui-components itself is untouched. Replaces the earlier
   blue/cyan/coral scheme entirely; this site only ever serves one
   palette (no light/dark toggle), so unlike that scheme's split
   :root+[data-theme="light"] / [data-theme="dark"] blocks, everything
   below is declared once in :root — that still wins over tokens/dark.css's
   own [data-theme="dark"] rules for a visitor whose OS prefers dark,
   since :root and an attribute selector are equal specificity and this
   file loads after both token stylesheets (last writer wins).
   Categorical badge colors (teal/cyan/indigo/violet below) are no longer
   four different hues — they're five lightness steps of the one sapphire
   (deepest to lightest: teal, primary, cyan, indigo, violet), each paired
   with whichever of the two text colors clears 4.5:1 against it. */
:root {
  --ui-color-primary-bg: #0f52ba;
  --ui-color-primary-fg: #ffffff;
  --ui-color-primary-bg-hover: #3672c9;

  --ui-color-teal-bg: #0b3d91;
  --ui-color-teal-fg: #ffffff;
  --ui-color-cyan-bg: #3672c9;
  --ui-color-cyan-fg: #ffffff;
  --ui-color-indigo-bg: #6f9adb;
  --ui-color-indigo-fg: #14181f;
  --ui-color-violet-bg: #a9c3ea;
  --ui-color-violet-fg: #14181f;

  /* The location chip (variant="neutral") is the one tag-shaped element
     deliberately left out of the sapphire family — it's a plain contact
     detail, not a content tag like the skill/language/interest/project
     badges above (all cycled through the sapphire gradient via
     `render::tag_style`), so it gets a warm stone neutral instead.
     `--ui-color-secondary-*` has no remaining caller on this site (project
     tags moved to the sapphire family too) but stays defined in case a
     future non-tag "plain label" element needs it, same as neutral does. */
  --ui-color-secondary-bg: #ded7c4;
  --ui-color-secondary-fg: #14181f;
  --ui-color-secondary-bg-hover: #d3cab2;
  --ui-color-neutral-bg: #ded7c4;
  --ui-color-neutral-fg: #14181f;
  --ui-color-neutral-bg-hover: #d3cab2;

  --ui-color-ghost-bg: #f1f1ef;
  --ui-color-ghost-fg: #14181f;
  --ui-color-ghost-bg-hover: #e6e4dc;
  --ui-color-ghost-border: #d9d3c3;

  --ui-color-surface-bg: #f1f1ef;
  --ui-color-surface-fg: #14181f;
  --ui-color-border-default: #d9d3c3;
  --ui-color-focus-ring: #0f52ba;
  --ui-color-link-visited: #0f52ba;

  /* Two accents outside the main token set, used sparingly: --wood for
     secondary/eyebrow text (the bio's role line, group labels between
     the tags), --gold as the one "pop" color, reserved for .cta-button/
     .cta-badge below — replaces the earlier warm-coral accent, which
     would have clashed against a blue/gold jewel-tone scheme. */
  --site-color-wood: #5c3a21;
  --ui-color-gold-bg: #b8962e;
  --ui-color-gold-fg: #14181f;
  --ui-color-gold-bg-hover: #cbab48;

  --site-font-display: Georgia, "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif;
}

/* One link color/underline policy for every link on the site, plain or
   themed: no underline until hover/keyboard-focus, never during or after
   a press. Two separate mechanisms are needed because two separate kinds
   of link exist here —
   - `<ui-link>` (nav, contact-row, project Source/Live): themed, real
     `<a part="control">` inside its shadow root, reached from here via
     `::part()` (the only piece of shadow DOM light-DOM CSS can select
     into) rather than a change in ui-components. Also drops its
     `:visited` color back to the same primary blue — this site wants one
     link color, not a second one for links you've already clicked.
   - plain `<a>` (deliberately unstyled prose links: the resume's
     education/organization mentions, render/home.rs) never got any color
     at all before this, just the browser's default link blue/visited
     purple — which is why they looked like a different color from the
     nav above them. This brings them in line without turning them into
     `<ui-link>`-style chips. */
ui-link::part(control),
a {
  text-decoration: none;
}

ui-link::part(control):visited,
a {
  color: var(--ui-color-primary-bg);
}

ui-link::part(control):hover,
ui-link::part(control):focus-visible,
a:hover,
a:focus-visible {
  text-decoration: underline;
}

ui-link::part(control):active,
a:active {
  text-decoration: none;
}

/* Scopes the gold accent (defined above) to specific CTA elements by
   overriding the *variant* custom properties those components already
   read (color.primary.bg/fg/bg-hover) — custom properties inherit
   through the shadow boundary, so setting them here on the host element
   repaints its own shadow-DOM control without any change in
   ui-components. Applied to the contact form's submit button and a
   project's "Featured" badge (render/contact.rs, render/projects.rs). */
.cta-button,
.cta-badge {
  --ui-color-primary-bg: var(--ui-color-gold-bg);
  --ui-color-primary-fg: var(--ui-color-gold-fg);
  --ui-color-primary-bg-hover: var(--ui-color-gold-bg-hover);
}

html, body {
  margin: 0;
  padding: 0;
}

/* Sticky footer: body is a full-viewport-height flex column, and
   <ui-page> (the one item in it actually allowed to grow — `flex: 1 1
   auto` below) absorbs any leftover space. <ui-navbar>/<ui-footer> keep
   their own natural height and end up pinned to the top/bottom edges
   without needing anything from them beyond just being flex siblings. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.5;
  background: var(--ui-color-surface-bg, #fff);
  color: var(--ui-color-surface-fg, #111827);
}

/* <ui-page> already provides its own max-width/centering/horizontal
   padding (size.page-max-width / size.page-gutter, in ui-components) —
   this only adds the vertical breathing room it doesn't, and the flex
   sizing that makes the sticky footer above work. */
ui-page {
  flex: 1 1 auto;
  padding-block: 1.5rem 3rem;
}

/* Serif reserved for the three heading levels (the site's "display"
   text) — nav/body copy stay in the sans-serif above, a deliberate
   pairing rather than leaning serif everywhere, which reads heavier than
   a resume-style page can carry. */
h1,
h2,
h3 {
  font-family: var(--site-font-display);
}

h1 {
  margin: 0 0 0.25rem;
  font-size: 1.85rem;
}

/* h2 is the site's one subheading tier (section headers like
   Skills/Experience, and via `color: inherit` on .opinion-list/
   .project-list's h2 a, each opinion/project title link too) — sapphire,
   same as every other brand/interactive color on the site now that the
   old cyan accent is gone. */
h2 {
  margin: 1.75rem 0 0.75rem;
  font-size: 1.3rem;
  color: var(--ui-color-primary-bg);
}

/* h3 sits one tier below h2 (skill-group labels like "NetSuite
   Development", between the section heading and its row of tags) — the
   wood accent instead of another shade of blue, so the tag rows
   underneath still read as the most saturated thing on the page. */
h3 {
  margin: 1rem 0 0.5rem;
  font-size: 1.05rem;
  color: var(--site-color-wood);
}

p.title {
  margin: 0 0 0.75rem;
  font-size: 1.1rem;
  font-style: italic;
  color: var(--site-color-wood);
}

p.bio {
  margin: 0 0 1rem;
  max-width: 42rem;
}

p.education {
  margin: 0 0 1rem;
  font-size: 0.95rem;
  font-weight: 600;
}

.contact-row {
  margin-bottom: 1.5rem;
}

/* The education seal/crest — a `<span>` with a `background-image`
   (render/home.rs's `logo_figure`), not an `<img>` with `object-fit:
   contain`: at certain browser zoom levels, an `object-fit`-scaled
   `<img>` rounds its destination rectangle to physical pixels slightly
   off from its container, showing up as a stray thin line along one or
   two edges — a `background-image` has no separate destination rect to
   round differently, so there's nothing to misalign. `background-size:
   contain` (not `cover`) for the same reason the old `object-fit: contain`
   was chosen: unlike the profile photo, a seal is the whole point of the
   image, so nothing about it should get cropped off to fill the box.
   Floated and gapped from the education line beside it by `<ui-media>`
   itself. */
/* `display: block` explicitly: `<ui-media>` floats whichever element
   carries `slot="figure"` and a `float` auto-blockifies its own element —
   fine when that's this span directly, but now that a linked seal/logo
   wraps it in an `<a slot="figure">` (render/home.rs's `logo_figure`),
   the span itself is no longer the slotted/blockified element, and an
   inline element's explicit width/height (below) are simply ignored —
   the bug that made linked logos disappear. Declaring it here instead of
   relying on the float means it renders the same size whether it's the
   direct slotted child or nested one level inside a link. */
.education-seal {
  display: block;
  width: 6rem;
  height: 6rem;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

/* A company logo inside an expanded experience entry (render/home.rs's
   experience_section) — same `background-image`/`contain` reasoning as
   `.education-seal` above, same `<ui-media>` float. Larger than the seal
   despite sitting beside denser content (a paragraph/list, not a single
   short line): most of these logos are wide/rectangular, not square, so
   `contain` already letterboxes them well short of this box's full
   height. */
/* `.entry-image` (an opinion/project's own illustrating image,
   render/opinions.rs, render/projects.rs) shares this exact size/fit —
   same `<ui-media>` float, same "not every source image is square"
   reasoning, just for a photo/screenshot instead of a company's own
   logo. */
.company-logo,
.entry-image {
  display: block;
  width: 9rem;
  height: 9rem;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

/* Opt-in circular color fill behind a seal/logo/entry image
   (`image_figure`, render/mod.rs) — for the minority whose source image
   has a transparent
   or near-page-background background and would otherwise be hard to make
   out. `background-color` and `background-image` both paint on the same
   `.education-seal`/`.company-logo` element — no separate wrapper-plus-
   inner-image pair (an earlier version of this nested them): a second,
   separately-painted element inset by padding was a plausible culprit
   for a stray rectangular line that showed up around the image at some
   browser zoom levels and not others (a fractional-pixel compositing
   seam at that inner element's own edge) even after switching from
   `<img>` to `background-image`. One element has no such edge to seam
   against. `border-radius` alone clips an element's own background to
   the round shape — no `overflow: hidden` needed for that (only for
   clipping a *child's* content, and there's no child here). */
.logo-badge {
  border-radius: 50%;
  box-sizing: border-box;
  background-origin: content-box;
}

/* Padding sized so a logo that fills its box edge-to-edge still clears
   this circle's *inscribed* circle instead of getting its corners cut off
   — each value is roughly (box size) &times; (1 &minus; 1/&radic;2) / 2,
   the inset needed to keep a full-bleed square's corners inside the
   circle, plus a little slack. Fixed units (not a percentage): CSS
   resolves a padding percentage against the *containing block's* width,
   not this element's own — on `.logo-badge` alone that meant 18% of the
   whole accordion column, not of the ~9rem badge, which produced
   padding far bigger than the box itself (box-sizing: border-box then
   has nowhere to put the padding but grow the box to fit it) and left the
   image with zero space to render into. One rule per size class since
   each box needs a different fixed inset. `background-origin: content-box`
   (above) is what makes this padding shrink the *image* instead of just
   adding dead space around it, the way it would for an ordinary box.
   `.company-logo`'s padding is intentionally left at its pre-9rem value
   (not scaled up alongside the box) — scaling both together kept the
   image:padding ratio constant, so the box grew but the logo read no
   bigger relative to its own whitespace. Leaving padding fixed means all
   of the box's growth becomes visible image. Technically a hair under
   the formula's safe minimum for 9rem (~1.32rem) now, so a logo whose
   source image is a perfectly full-bleed square could clip a sliver off
   its corners — an edge case none of the current logos hit. */
.education-seal.logo-badge {
  padding: 1rem;
}

.company-logo.logo-badge,
.entry-image.logo-badge {
  padding: 1.25rem;
}

/* A contact-row link rendered as a brand icon instead of plain text (e.g.
   LinkedIn, render/home.rs). `width`/`height` override the icon's own
   `1em` sizing (`ui_ssr::icons::LINKEDIN`'s `<svg>` attributes — CSS
   properties always win over presentation attributes) to double it
   relative to the surrounding text/badges, which otherwise rendered it
   too small to read as a recognizable brand mark. `vertical-align` nudges
   the now-larger glyph to sit on the same baseline as its neighbors
   instead of the slight inline-image offset browsers give SVGs by
   default. */
.icon-link svg {
  width: 2em;
  height: 2em;
  vertical-align: -0.35em;
}

/* The profile photo's own size/crop/rounding — it's floated and gapped
   from the flowing name/title/bio/education beside it by <ui-media> itself
   (render/home.rs), not by anything here. */
.profile-avatar {
  width: 20rem;
  height: 25rem;
  object-fit: cover;
  border-radius: var(--ui-radius-lg, 0.75rem);
}

@media (max-width: 40rem) {
  .profile-avatar {
    width: 14rem;
    height: 17.5rem;
  }
}

/* The logo replacing the plain-text site name in the navbar (see
   page_shell's own `brand` variable) — capped to a sane height so
   whatever aspect ratio the provided image has doesn't blow out the
   navbar's own row height. Doubled from an earlier 2rem: at that size it
   read as an afterthought next to the nav links beside it. */
.site-logo-link {
  display: inline-flex;
  align-items: center;
}

.site-logo {
  height: 4rem;
  width: auto;
  display: block;
}

/* The plain-text brand name (`page_shell`'s `brand` fallback when no
   logo is set) and the nav links beside it — <ui-navbar> itself declares
   no type-scale token, so both otherwise render at the page's plain 1rem
   body size, reading as too small next to a doubled logo. Both scaled up
   to match. `ui-navbar ui-link::part(control)`, not a change in
   ui-components: `::part()` is the one piece of shadow DOM light-DOM CSS
   can select into (see the link/visited-color rules above), reached
   through an ordinary descendant combinator since the slotted `<ui-link>`
   elements are still real light-DOM children of `<ui-navbar>`.
   Brand name backed off from a full 2rem match (read as oversized/cramped
   next to the logo) to 1.5rem with added letter-spacing to compensate for
   the smaller size reading tighter; the links themselves backed off from
   an initial 2rem to 1.75rem for the same reason, reading slightly heavy
   at full size. */
ui-navbar strong {
  font-size: 1.5rem;
  letter-spacing: 0.04em;
}

ui-navbar ui-link::part(control) {
  font-size: 1.75rem;
}

/* Two separate gaps inside the navbar, both driven by the same
   `--ui-space-md` token and both overridden the same way (scoped to the
   specific host element whose shadow tree reads it, rather than
   redefined at `:root` and widening gaps elsewhere that were never meant
   to move) —
   - `<ui-navbar>`'s own shadow CSS uses it for the gap *between* its two
     flex items: the logo (default slot) and the "links" slot as a whole.
     That's the logo-to-"Resume" gap; unaffected here is its padding
     (navbar.component.kdl's second `padding` declaration reads
     `--ui-space-lg` instead, cascading over the first).
   - the "links" slot's own content, one `<ui-stack direction="row"
     slot="links">` (render/mod.rs's `nav`), reads the same token for the
     gap *between* Resume/Opinions/Projects/Contact themselves (see
     stack.component.kdl).
   Setting it on `<ui-navbar>` also inherits down into the nested
   `<ui-stack>`'s shadow tree (custom properties inherit through nested
   shadow boundaries same as `.cta-button` above relies on) — harmless
   here since the more specific rule below re-overrides it for that
   element specifically. */
ui-navbar {
  --ui-space-md: 2.5rem;
}

ui-navbar ui-stack[slot="links"] {
  --ui-space-md: 3rem;
}

/* The current page remains obvious when the mobile navigation becomes a
   compact text-tab row below. This is intentionally text, not an icon-only
   menu: every destination stays visible and understandable without first
   knowing what a hamburger glyph means. */
ui-navbar ui-link[aria-current="page"]::part(control) {
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 0.12em;
  text-underline-offset: 0.35em;
}

/* A skill badge wrapped in a link (see render/home.rs's skill_badge) —
   the link itself should read as "this badge is clickable", not as
   default underlined blue text wrapping a colored chip. */
.skill-link {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
}

/* Without this, `.skill-link` (a plain `<a>` wrapping a skill badge that
   has a url — render/home.rs's skill_badge) still picked up the global
   `a:hover { text-decoration: underline }` rule above on hover/focus,
   since a class-plus-pseudo-class selector always outranks that rule's
   element-plus-pseudo-class one regardless of source order. That underlined
   exactly the skill tags with a url set and left every other tag on the
   site (plain skill/language/interest/project chips, never wrapped in an
   `<a>` at all) unaffected — the inconsistent underline-on-hover this
   overrides. */
.skill-link:hover,
.skill-link:focus-visible {
  text-decoration: none;
}

/* An experience entry's accordion-item label (render/home.rs's
   experience_section) — role/organization/period/location each get their
   own element instead of being run together with a dash and parens, so a
   reader can scan any one of the four independently. Stacks title over
   meta; both wrap onto their own line inside the accordion's summary at
   any width instead of being squeezed. */
.experience-header {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.experience-title {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem;
}

.experience-title .role {
  font-size: 1.05rem;
}

/* "@" between role and org (Title @ Company) reads as a single visual
   unit rather than two independently laid-out fields — muted so the
   role/org text stays the two things a reader actually scans. */
.at-separator {
  color: var(--site-color-wood);
  opacity: 0.7;
}

.experience-title .org {
  color: var(--ui-color-primary-bg);
}

.experience-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
}

/* The accordion component reserves space on the left for its disclosure
   marker, but its default-slot content otherwise runs all the way to the
   right border. Give each detail block a matching small end gutter; the
   label is in the named slot and is deliberately excluded because its
   shadow summary already has its own two-sided padding. */
ui-accordion-item > :not([slot="label"]) {
  margin-inline-end: 0.75rem;
}

/* Each meta item carries its own small caption (`.meta-label`) above the
   value — the label is what replaces the parenthesis/dash punctuation
   that used to tell a reader "this is the period, this is the location";
   now the field names itself instead of relying on position. */
.meta-item {
  display: flex;
  flex-direction: column;
  font-size: 0.85rem;
}

.meta-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--site-color-wood);
}

/* Home page: a wide main column plus a narrower sidebar
   (languages/interests — secondary content, see render/home.rs) side by
   side on desktop. On mobile there's no room for two columns without
   either one going unreadably narrow, so it collapses to a single
   column and the sidebar just falls in after the main content instead —
   still fully readable, just no longer "to the side". */
.home-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
}

@media (min-width: 48rem) {
  .home-layout {
    grid-template-columns: minmax(0, 2fr) minmax(12rem, 1fr);
    align-items: start;
  }
}

/* An opinion/project accordion-item's collapsed label (render/opinions.rs,
   render/projects.rs) — title stacked above its badges/tags/rating, same
   "each field reads independently, wraps at any width" shape as
   .experience-header/.experience-title above, just for a flatter
   title+meta pair instead of four fields. */
.item-header {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.item-title {
  font-size: 1.05rem;
  font-weight: 600;
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 32rem;
}

.contact-form label {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  font-size: 0.9rem;
  font-weight: 600;
}

.contact-form input,
.contact-form textarea {
  font: inherit;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--ui-color-border-default, #e5e7eb);
  border-radius: 0.5rem;
  background: var(--ui-color-surface-bg, #fff);
  color: var(--ui-color-surface-fg, #111827);
}

@media (max-width: 40rem) {
  /* Keep all four destinations visible on one compact row instead of
     replacing them with a hamburger menu. The monogram and type shrink,
     while the links retain a 2.5rem-high tap target; this cuts the sticky
     header to roughly half its desktop height without hiding navigation. */
  ui-navbar {
    --ui-space-md: 0.5rem;
    --ui-space-lg: 0.625rem;
  }

  ui-navbar::part(control) {
    flex-wrap: nowrap;
    gap: 0.625rem;
    padding: 0.5rem 0.75rem;
  }

  .site-logo {
    height: 2.5rem;
  }

  ui-navbar strong {
    font-size: 1rem;
  }

  ui-navbar ui-stack[slot="links"] {
    flex: 1 1 auto;
    min-width: 0;
    --ui-space-md: clamp(0.25rem, 2vw, 0.625rem);
  }

  ui-navbar ui-stack[slot="links"]::part(control) {
    flex-wrap: nowrap;
    justify-content: space-between;
  }

  ui-navbar ui-link::part(control) {
    display: inline-flex;
    align-items: center;
    min-height: 2.5rem;
    padding-inline: 0.1rem;
    font-size: clamp(0.82rem, 3.5vw, 1rem);
    white-space: nowrap;
  }

  /* A floated 9rem company logo plus a browser-default indented list left
     only a narrow ribbon for highlight text on small phones. Stack the
     logo above the copy and tighten list indentation so bullets keep the
     full accordion width. */
  .experience-media > [slot="figure"] {
    float: none;
    display: inline-block;
    margin: 0 0 0.75rem;
  }

  .experience-media .company-logo {
    width: 7rem;
    height: 7rem;
  }

  ui-accordion-item {
    --ui-space-sm: 0.4rem;
    --ui-space-md: 0.5rem;
  }

  ui-accordion-item ul {
    margin-block: 0.75rem 0;
    padding-inline-start: 1.25rem;
  }

  ui-page {
    padding-block: 1rem 2.5rem;
  }

  h1 {
    font-size: 1.4rem;
  }
}
