html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

html {
  position: relative;
  min-height: 100%;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
  color: var(--bs-secondary-color);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
  text-align: start;
}
/* ---------- OMS Education and Consulting: single-page site ---------- */

:root {
  --oms-navy: #1f3a5f;
  --oms-navy-soft: rgba(31, 58, 95, 0.08);
  --oms-teal: #2b7a78;
  --oms-teal-soft: rgba(43, 122, 120, 0.10);
  --oms-gold: #d89b3c;
  --oms-text: #26313d;
  --oms-muted: #6c7680;
  --oms-card-shadow: 0 10px 30px rgba(31, 58, 95, 0.10);
  --font-display: "Lora", Georgia, "Times New Roman", serif;
  --font-body: "Inter", Calibri, Arial, sans-serif;
  --oms-page-gradient: linear-gradient(180deg, #ffffff 0%, #f3f7f7 45%, #eef4f3 100%);
}

html, body {
  /* min-height, not height -- a fixed height clips the body (and the
     gradient background painted on it) to exactly one viewport, so a
     tall page (e.g. once the contact form pushed the Contact panel past
     that) overflows past the gradient instead of the gradient growing
     to cover it. */
  min-height: 100%;
  /* 100% (above) resolves against the *small* viewport (address bar
     shown) on Chrome/Brave for Android, and doesn't grow after the fact
     when the address bar later auto-hides on scroll -- the actual
     visible area gets taller, but this computed height doesn't, so a
     bare strip of unpainted browser canvas (plain white, no gradient)
     appears below wherever the page's own content happened to be
     shorter than that newly revealed height. 100dvh is the dynamic
     viewport height unit made for exactly this: browsers recompute it
     live as their UI chrome shows/hides, so the gradient always reaches
     the actual bottom of the visible area. Kept as a second declaration
     rather than replacing the line above -- a browser that doesn't
     support dvh yet just ignores this one and keeps the 100% fallback. */
  min-height: 100dvh;
  margin: 0;
  /* Pulling past the top/bottom on a touchscreen (rubber-band/overscroll,
     not a Safari-specific thing -- Chrome on Android and Chrome DevTools'
     device emulation do it too) reveals a plain fill color behind the
     page, and that fill comes from background-color specifically, not
     background-image -- a gradient can't be "extended" into a region
     that's synthesized rather than painted. The shorthand below
     ("background: <gradient>") sets background-image but resets
     background-color to transparent, which is why that area showed
     white regardless of the gradient. Setting an explicit fallback color
     (this gradient's own darkest stop) alongside the gradient image is
     what actually fixes it -- on both html and body, since either can be
     the one a given browser samples. */
  background-color: #eef4f3;
  background-image: var(--oms-page-gradient);
}

body {
  font-family: var(--font-body);
  color: var(--oms-text);
}

/* Mobile Chrome/Safari paint a translucent flash (usually cyan-ish, from
   the browser's own default) over whatever was tapped -- any link, and
   anything clickable enough to look like one, including an <img> wrapped
   in an <a>. Set on html rather than listing every clickable selector by
   name, so nothing tappable is ever missed, this one included. */
html {
  -webkit-tap-highlight-color: transparent;
}

/* Shown at the very top of the page whenever the visitor is logged in (see
   Index.cshtml) -- a quick way back into /Admin without having to remember
   or type the URL. The toggle button collapses it down to a slim strip
   (rather than hiding it entirely) so it's still there to bring back, but
   stops taking up space once someone's seen it. Once collapsed, hovering
   the strip for a moment (site.js) reveals it again the way a browser's
   own auto-hiding bars do, via the .peeking class below -- clicking the
   toggle is still there as the deliberate, permanent way to expand it
   again (or to collapse it back down), but isn't the only way in. */
.admin-banner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.6rem 2.75rem;
  background: linear-gradient(135deg, var(--oms-navy), var(--oms-teal));
  text-align: center;
  transition: padding 0.2s ease;
}

.admin-banner.collapsed {
  padding: 0.3rem 2.75rem;
  /* Signals the strip is hoverable (it reveals itself on hover) right
     away, rather than only once the reveal itself has already happened. */
  cursor: pointer;
}

/* Collapsed down to its slim strip, neither the text nor the toggle
   button are shown -- there's nothing left to clip or poke out, so the
   strip can actually be slim. Both reappear together once .peeking
   (site.js, on hover) or a real expand brings back the full padding. */
.admin-banner.collapsed:not(.peeking) .admin-banner-text,
.admin-banner.collapsed:not(.peeking) .admin-banner-toggle {
  display: none;
}

.admin-banner.collapsed.peeking {
  padding: 0.6rem 2.75rem;
}

.admin-banner-text {
  color: #ffffff;
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
}

.admin-banner-text:hover {
  text-decoration: underline;
}

.admin-banner-toggle {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  background: rgba(255, 255, 255, 0.15);
  border: none;
  border-radius: 50%;
  color: #ffffff;
  font-size: 0.65rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease;
}

.admin-banner-toggle:hover {
  background: rgba(255, 255, 255, 0.3);
}

/* The pin emoji (shown only while collapsed -- see site.js) renders much
   smaller than the plain arrow glyph at the same font-size, so it needs
   its own larger size to actually fill the button. */
.admin-banner.collapsed .admin-banner-toggle {
  font-size: 1rem;
}

/* Shown instead of the whole page when SiteConfig.IsDemoMode is on and no
   one's logged in (see Index.cshtml) -- centers a single card in the
   viewport rather than trying to fit the site's usual header/hero/nav
   layout around one message. */
.demo-mode-notice {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  box-sizing: border-box;
  text-align: center;
}

.demo-mode-notice p {
  max-width: 480px;
  margin: 0;
  padding: 2rem 2.25rem;
  background: #ffffff;
  border-radius: 16px;
  box-shadow: var(--oms-card-shadow);
  font-size: 1.05rem;
  line-height: 1.7;
  color: var(--oms-text);
}

.demo-mode-notice strong {
  color: var(--oms-navy);
}

.demo-mode-notice a {
  color: var(--oms-teal);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid rgba(43, 122, 120, 0.35);
}

.demo-mode-notice a:hover {
  border-bottom-color: var(--oms-teal);
}

/* .hero-wrap's height and .hero's transform both animate over 0.5s (below)
   when a nav item is chosen or the logo is clicked. The content panel
   shows/hides the instant that happens, though, not after the hero
   finishes collapsing/expanding -- so for that half-second the page is
   briefly taller (collapsing) or shorter (expanding) than it settles at,
   which flashes a scrollbar on and straight back off. site.js adds this
   class for exactly that half-second to suppress the flash without
   otherwise changing how the page scrolls. */
body.hero-transitioning {
  overflow: hidden;
}

/* ----- shared icon-in-circle motif, used in the nav and again as a
   small marker on each content panel's heading ----- */
.icon-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--oms-navy-soft), var(--oms-teal-soft));
  transition: background 0.2s ease, transform 0.2s ease;
}

.icon-badge--nav {
  width: 68px;
  height: 68px;
}

.icon-badge--nav img {
  width: 32px;
  height: 32px;
}

.icon-badge--panel {
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
}

.icon-badge--panel img {
  width: 24px;
  height: 24px;
}

/* Two copies of this markup exist (see Index.cshtml) -- .lang-switcher--grid
   sits in the third column of .site-header's grid below, in normal
   document flow like the logo and title next to it; .lang-switcher--corner
   is a separate copy, absolutely positioned in the page's top-right
   corner instead. Only one is ever shown at a time -- --grid above the
   mobile breakpoint, --corner below it (see that media query) -- so
   exactly one heads to the accessibility tree too, since a display:none
   element is skipped by it. */
.lang-switcher {
  display: flex;
  gap: 0.5rem;
}

.lang-switcher--corner {
  display: none;
}

.lang-flag {
  display: block;
  width: 38px;
  height: 24px;
  border-radius: 3px;
  overflow: hidden;
  box-shadow: 0 2px 6px rgba(31, 58, 95, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.7);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.lang-flag svg {
  display: block;
  width: 100%;
  height: 100%;
}

.lang-flag:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(31, 58, 95, 0.35);
}

.site-header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  column-gap: 1em;
  align-items: center;
  padding: 1.5rem 2rem 1rem;
}

.site-logo {
  height: 64px;
  width: auto;
  cursor: pointer;
}

.site-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.7rem;
  margin: 0;
  color: var(--oms-navy);
  letter-spacing: 0.01em;
  cursor: pointer;
}

/* Hero: sits above the nav on the home state, sized to whatever room is
   left in the browser window after the header and nav, so header + hero
   + nav always fit within one screen with no scrolling needed to reach
   the buttons. When a nav item is chosen it slides up and collapses out
   of the document flow - the nav below it rises to take its place. */
.hero-wrap {
  padding: 0 2rem;
  overflow: hidden;
  height: clamp(160px, calc(100vh - 240px), 560px);
  transition: height 0.5s ease;
  box-sizing: border-box;
}

.hero-wrap.collapsed {
  height: 0;
}

.hero {
  position: relative;
  height: 100%;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: var(--oms-card-shadow);
  transition: transform 0.5s ease;
}

.hero-wrap.collapsed .hero {
  transform: translateY(-100%);
}

.hero-slide {
  position: absolute;
  /* A couple of pixels wider than a plain "inset: 0" on each side --
     during the push transition below, subpixel rounding on the transform
     can otherwise leave a hairline gap of whatever's behind the hero
     (the page background) showing between the outgoing and incoming
     picture for a frame or two. The couple of pixels of overlap this
     gives each slide hides that seam; .hero's own overflow: hidden
     still clips the extra edge everywhere else. */
  top: 0;
  right: -2px;
  bottom: 0;
  left: -2px;
  /* Parked just off the left edge -- ready to be pushed into view. Every
     slide sits here by default; site.js only ever moves the one that's
     about to become active (parking it here without a transition first,
     in case it's actually sitting off the *right* edge from its last
     turn) and the one that's leaving (below). */
  transform: translateX(-100%);
  transition: transform 2s ease;
  z-index: 0;
}

.hero-slide.active {
  transform: translateX(0);
  z-index: 2;
}

/* The slide that was active a moment ago: pushed the rest of the way off
   to the right while the new .active slide (above) pushes in from the
   left at the same time -- a "push" carousel transition rather than the
   two crossfading in place. site.js removes this once the push finishes
   and parks the slide back at the left edge (again without a transition)
   so it's ready for its next turn. */
.hero-slide.leaving {
  transform: translateX(100%);
  z-index: 1;
}

/* Used only for the instant, one-off jumps described above -- never for
   anything the visitor should see animate. */
.hero-slide.no-transition {
  transition: none;
}

/* <picture> is inline by default, which -- like the <img> it wraps --
   leaves a few stray pixels of baseline gap beneath it if not overridden. */
.hero-slide picture {
  display: block;
  width: 100%;
  height: 100%;
}

.hero-slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(15, 32, 51, 0.85) 0%, rgba(15, 32, 51, 0.35) 35%, rgba(15, 32, 51, 0) 65%);
}

.hero-caption {
  position: absolute;
  right: 1.75rem;
  bottom: 1.5rem;
  max-width: 70%;
  color: #ffffff;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.1rem, 2.4vw, 1.6rem);
  letter-spacing: 0.01em;
  text-align: right;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
  /* Default/reset state: hidden and nudged toward the right, ready for
     the next slide-in-from-right entrance. site.js always routes an
     exit through .is-leaving (drop off the bottom) first and only ever
     resets back to this plain state afterwards, at which point the
     caption is already invisible -- so this rule's own transition is
     just a non-janky fallback, not something normally seen in use. */
  opacity: 0;
  transform: translateX(2rem);
  transition: opacity 0.3s ease, transform 0s linear 0.3s;
}

/* Step two: once .hero-slide's own 2s push transition (above) has
   finished, slide the caption in from the right. The 2s delay here is
   what makes this wait for the image transition rather than run
   alongside it.
   Driven by a class of its own (site.js), rather than keyed off
   .hero-slide.active directly, so the very first slide on page load can
   start with its caption genuinely hidden and play this same entrance
   once, instead of just appearing already in place. */
.hero-caption.is-visible {
  opacity: 1;
  transform: translateX(0);
  transition: opacity 0.6s ease 2s, transform 0.6s ease 2s;
}

/* Step three: before the slide starts fading out, drop the caption off
   the bottom of the frame instead -- the .hero container's
   overflow: hidden clips it once it's past the edge. site.js waits for
   this to finish before starting the next push transition, so the
   caption is always fully gone before the image itself starts moving. */
.hero-caption.is-leaving {
  opacity: 0;
  transform: translateY(2.5rem);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.site-nav {
  display: flex;
  justify-content: space-around;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 1.25rem 2rem 1.5rem;
}

.nav-item {
  background: none;
  border: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  cursor: pointer;
  padding: 0.5rem 1rem;
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--oms-text);
}

.nav-item:hover .icon-badge {
  transform: translateY(-2px);
  background: linear-gradient(135deg, var(--oms-navy-soft), var(--oms-teal-soft) 120%);
}

.site-nav.has-active .nav-item:not(.active) .icon-badge {
  opacity: 0.45;
  filter: grayscale(40%);
}

.nav-item.active .icon-badge {
  background: linear-gradient(135deg, var(--oms-gold), #f0c274);
  box-shadow: 0 6px 16px rgba(216, 155, 60, 0.35);
}

.nav-item.active .nav-label {
  font-weight: 700;
  color: var(--oms-navy);
}

.site-content {
  max-width: 1000px;
  margin: 0 auto;
  /* Kept as a custom property (as well as literally below) purely so
     .panel-slide further down can read the same number back -- see the
     comment on .site-nav.has-active ~ .site-content for why. */
  --site-content-pad-x: 2rem;
  padding: 0 var(--site-content-pad-x);
  /* Positioning context for the panel-to-panel push transition below --
     harmless the rest of the time, since a plain .content-panel is never
     itself positioned. */
  position: relative;
}

/* Only set while site.js is actively pushing one panel out and the next
   one in (see .panel-slide below) -- clips the two panels to this box
   while they're briefly wider than one viewport-width apart, and is
   lifted again as soon as the swap finishes so a panel's own box-shadow
   isn't clipped the rest of the time. The height transition lets
   site.js animate this element's own height from the outgoing panel's
   to the incoming one's over the same span as the push below, rather
   than pinning it to the taller of the two for the whole swap and then
   snapping straight to the right height the instant it ends. */
.site-content.panel-transitioning {
  overflow: hidden;
  transition: height 0.4s ease;
}

/* No content panel is shown until a nav item is picked (the home/hero
   state) -- .content-panel below is display:none for all of them at that
   point, but this element's own top/bottom padding still took up space
   regardless, which was enough on its own to push the page past one
   viewport and show a scrollbar even though nothing was actually visible
   down there. Restore the padding only once a panel is showing.
   --site-content-pad-top is kept as a custom property (as well as
   literally, below) for the same reason as --site-content-pad-x above --
   .panel-slide further down reads both back, to sit exactly where a
   plain static .content-panel would: top/left/right on an absolutely
   positioned element are measured from its container's *padding* edge,
   not its content edge, so "top: 0" alone would land above this
   padding-top entirely, about 1.75rem higher than a normal panel ever
   sits -- which is exactly the jump this was causing. */
.site-nav.has-active ~ .site-content {
  --site-content-pad-top: 1.75rem;
  padding: var(--site-content-pad-top) var(--site-content-pad-x) 3rem;
}

.content-panel {
  display: none;
  background: #ffffff;
  border-radius: 16px;
  box-shadow: var(--oms-card-shadow);
  padding: 2rem 2.25rem 2.25rem;
}

.content-panel.active {
  display: block;
}

/* The plain fade-in below is only for arriving from/returning to the
   hero (home) state -- .panel-slide (further down) is site.js's own
   marker for the other kind of transition, panel-to-panel, which gets
   the push transition instead, so this is skipped whenever that's the
   one playing. */
.content-panel.active:not(.panel-slide) {
  animation: oms-fade-in 0.3s ease;
}

@keyframes oms-fade-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ----- panel-to-panel push transition (site.js's slideToPanel) -----
   Mirrors the hero slideshow's own push transition above (.hero-slide) --
   same idea, same left/right convention (new content enters from the
   left, old content exits to the right), just for two content panels
   swapping instead of two hero images. Only applied to the two panels
   actually involved in a given switch, and only for the swap's own
   duration -- see slideToPanel in site.js for the full sequence. */
.content-panel.panel-slide {
  position: absolute;
  top: var(--site-content-pad-top, 0);
  left: var(--site-content-pad-x, 0);
  right: var(--site-content-pad-x, 0);
  z-index: 0;
}

.content-panel.panel-slide-transition {
  transition: transform 0.4s ease;
}

.content-panel.panel-slide-parked-left {
  transform: translateX(-100%);
}

.content-panel.panel-slide-active {
  transform: translateX(0);
  z-index: 2;
}

.content-panel.panel-slide-out {
  transform: translateX(100%);
  z-index: 1;
}

.panel-heading {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1.25rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid rgba(31, 58, 95, 0.10);
}

.panel-heading h2 {
  font-family: var(--font-display);
  font-size: 1.55rem;
  color: var(--oms-navy);
  margin: 0;
}

/* [h1]/[h2]/[h3] in user-authored BBCode content (see BbCodeRenderer)
   render as <h3>/<h4>/<h5> respectively, not <h1>/<h2>/<h3> -- this page
   already has its own <h1> (the site title) and <h2>s (each section's own
   heading, in .panel-heading above), so the actual heading levels used
   here are shifted down to avoid doubling either up. No special selector
   is needed to keep this separate from .panel-heading's own <h2> the way
   an equivalent rule for an actual h1/h2 would have needed -- nothing
   else in a panel is ever an h3, h4 or h5. The three tiers below still
   read biggest-to-smallest, matching what picking "Heading 1/2/3" in the
   editor suggests, even though the underlying tags don't. */
.content-panel h3 {
  font-family: var(--font-display);
  font-size: 1.75rem;
  color: var(--oms-navy);
  margin: 0 0 0.6rem;
}

.content-panel h4 {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--oms-navy);
  margin: 0 0 0.6rem;
}

.content-panel h5 {
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--oms-teal);
  margin: 0 0 0.5rem;
}

.content-panel p,
.content-panel li {
  line-height: 1.7;
  font-size: 1rem;
}

.materials-list {
  list-style: disc;
  padding-left: 1.25rem;
  line-height: 2;
}

.materials-list::marker,
.materials-list li::marker {
  color: var(--oms-teal);
}

.materials-list a {
  color: var(--oms-teal);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid rgba(43, 122, 120, 0.35);
}

.materials-list a:hover {
  border-bottom-color: var(--oms-teal);
}

.placeholder-copy {
  color: var(--oms-muted);
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  max-width: 480px;
  margin: 1.5rem auto 0;
}

.contact-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--oms-text);
}

    .contact-field.contact-second {
        display: none;
    }

    .contact-field input,
    .contact-field textarea {
        padding: 0.65rem 0.85rem;
        font-size: 1rem;
        font-family: var(--font-body);
        font-weight: 400;
        color: var(--oms-text);
        border: 1px solid rgba(31, 58, 95, 0.2);
        border-radius: 8px;
        box-sizing: border-box;
    }

.contact-field input:focus,
.contact-field textarea:focus {
  outline: none;
  border-color: var(--oms-teal);
  box-shadow: 0 0 0 3px rgba(43, 122, 120, 0.15);
}

.contact-field textarea {
  resize: vertical;
  min-height: 120px;
  font-family: inherit;
}

.contact-submit {
  align-self: flex-end;
  padding: 0.7rem 1.5rem;
  font-size: 1rem;
  font-weight: 600;
  font-family: inherit;
  color: #ffffff;
  background: var(--oms-navy);
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s ease;
}

.contact-submit:hover {
  background: #16283f;
}

@media (max-width: 576px) {
  .lang-switcher--grid {
    display: none;
  }

  .lang-switcher--corner {
    display: flex;
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
    margin-top: 1em;
    margin-right: 0.5em;
    z-index: 50;
  }

  .site-header {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
    gap: 0.75rem;
    padding: 1rem;
  }

  .site-title {
    font-size: 1.5rem;
  }

  .hero-wrap {
    padding: 0 1rem;
    height: clamp(120px, calc(100vh - 360px), 380px);
  }

  .hero-caption {
    right: 1rem;
    bottom: 1rem;
    max-width: 80%;
    font-size: 1rem;
  }

  .site-nav {
    padding: 1rem 1rem 1.25rem;
    gap: 0.25rem;
  }

  .icon-badge--nav {
    width: 52px;
    height: 52px;
  }

  .icon-badge--nav img {
    width: 24px;
    height: 24px;
  }

  .nav-item {
    padding: 0.25rem 0.5rem;
  }

  .nav-label {
    font-size: 0.85rem;
  }

  .site-content {
    --site-content-pad-x: 1.25rem;
    padding: 0 var(--site-content-pad-x);
  }

  .site-nav.has-active ~ .site-content {
    --site-content-pad-top: 1rem;
    padding: var(--site-content-pad-top) var(--site-content-pad-x) 2rem;
  }

  .content-panel {
    padding: 1.5rem 1.5rem 1.75rem;
  }
}
