/* =============================================================================
   LogansDev — single source stylesheet
   -----------------------------------------------------------------------------
   Consolidated 2026-08-09 from css/index.css + modal.css + admin.css. Each of
   those declared its own palette, so a remodel of the public site never reached
   the portal or admin: the public --primary was #16a34a while the entire
   authenticated area sat on #059669.

   RULES FOR EDITING
   1. The :root block below is the only palette. Never redeclare a token.
   2. Every page loads this file and no other. Do not add a second stylesheet.
   3. Text placed directly on --forest-tint must be light. Dark ink measures
      3.04:1 there; the cream --tan-text is 5.21:1 and white is 5.88:1.
   4. Rules that legitimately differ between the public site, the portal and
      admin are scoped with a body class, not left to cascade order.

   PAGE SCOPES   body.page-portal -> portal, login, register, credentials
                 body.page-admin  -> admin
                 (no class)       -> index, privacy, terms
   ============================================================================= */



/* ══════════════════════════════════════════════════════════════════
   TOKENS — the single palette
   ══════════════════════════════════════════════════════════════════ */


:root {
  --primary:        #16a34a;
  --primary-dark:   #15803d;
  --primary-darker: #14532d;
  --primary-light:  #f0fdf4;
  --primary-tint:   #dcfce7;
  --accent:        #f59e0b;
  --accent-dark:   #d97706;
  --heading:       #0f172a;
  --body:          #475569;
  /* Forest green section background. Darkened from #5ca484 on 2026-08-09: the
     cream --tan-text sitting on it measured 2.62:1, below the WCAG AA 4.5:1
     floor. At #376f57 that copy is 5.21:1 and white is 5.88:1. Keep any text
     placed directly on this background light — dark ink is only 3.04:1 here. */
  --forest-tint:      #376f57;
  --tan-text:      #f8f1dc;
  --bg-subtle:     #f2f7f3;
  --border:        #e2e8f0;
  --white:         #ffffff;
  --footer-bg:     #0f172a;
  --footer-text:   #94a3b8;
  --font:          'Hanken Grotesk', Arial, Helvetica, sans-serif;
  --radius:        10px;
  --shadow:        0 4px 20px rgba(0, 0, 0, 0.07);
  --shadow-lg:     0 8px 36px rgba(0, 0, 0, 0.12);

  /* Surfaces that flip with the theme. Declared here so the block above stays
     the single palette; the light theme only reassigns these four. */
  --footer-divider: rgba(255, 255, 255, 0.07);
  --on-forest:      var(--white);      /* links/emphasis on --forest-tint     */
  --on-footer-strong: var(--white);    /* wordmark + link hover in the footer */
  --footer-chevron: #86efac;           /* logo chevron on the footer surface  */
  /* Navbar surface. Translucent because the bar blurs whatever scrolls
     under it, so the tint reads over the hero illustration too. */
  --nav-bg:         rgba(240, 253, 244, 0.96);   /* --primary-light, tinted */
  /* Every page/card surface that used to be pure white. The tint is easier
     on the eye over a long read than #ffffff. --white itself stays white:
     it is still the ink on green buttons and on the footer. */
  --surface:        #f0fdf4;
  --footer-link:      var(--primary);
  --footer-link-hover: #4ade80;
  --footer-dev:       var(--primary);  /* the "Dev" half of the footer logo   */
  /* Ink on --forest-tint. --primary is only 1.78:1 there, so the check icons
     use mint; the hairline rule is a translucent white. */
  --on-forest-strong: var(--white);
  --on-forest-icon:   #86efac;
  --on-forest-rule:   rgba(255, 255, 255, 0.18);
  --on-forest-chip:   rgba(255, 255, 255, 0.15);
  --on-forest-chip-hover: rgba(255, 255, 255, 0.25);
}


/* ---------- Light theme ----------------------------------------------------
   The palette above is the dark scheme and stays the default: with no
   data-theme attribute the site renders exactly as before. Setting
   data-theme="light" on <html> reassigns the two dark surfaces
   (--footer-bg, --forest-tint) and the ink that sits on them.

   Contrast on the reassigned pairs, all above the WCAG AA 4.5:1 floor:
     --tan-text  #14532d on --forest-tint #eef7f1  -> 9.7:1
     --footer-text #475569 on --footer-bg #f1f5f9  -> 7.4:1
     --heading   #0f172a on --footer-bg #f1f5f9    -> 16.4:1
   -------------------------------------------------------------------------- */

:root[data-theme="light"] {
  --forest-tint:      #eef7f1;
  --tan-text:         #14532d;
  --footer-bg:        #f1f5f9;
  --footer-text:      #475569;
  --bg-subtle:        #f8fafc;

  --footer-divider:   rgba(15, 23, 42, 0.10);
  --on-forest:        var(--primary-darker);
  --on-footer-strong: var(--heading);
  --footer-chevron:   var(--primary-darker);
  --nav-bg:           rgba(255, 255, 255, 0.96);
  --surface:          var(--white);
  /* --primary is only 3.01:1 on #f1f5f9 and #4ade80 is 1.59:1, so the footer
     links step down a shade rather than carrying the dark-theme greens over. */
  --footer-link:      var(--primary-dark);
  --footer-link-hover: var(--primary-darker);
  --footer-dev:       var(--primary-dark);
  --on-forest-strong: var(--heading);
  --on-forest-icon:   var(--primary-dark);
  --on-forest-rule:   rgba(15, 23, 42, 0.12);
  --on-forest-chip:   rgba(15, 23, 42, 0.06);
  --on-forest-chip-hover: rgba(15, 23, 42, 0.12);

  --shadow:           0 4px 20px rgba(15, 23, 42, 0.06);
  --shadow-lg:        0 8px 36px rgba(15, 23, 42, 0.10);
}


/* ══════════════════════════════════════════════════════════════════
   PUBLIC SITE — index.html, privacy.html, terms.html
   ══════════════════════════════════════════════════════════════════ */


/* ---------- Reset & Base ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; scrollbar-gutter: stable; }  /* merged: app files set scrollbar-gutter */

body {
  font-family: var(--font);
  color: var(--body);
  line-height: 1.65;
  background-color: var(--surface);
}

img { max-width: 100%; display: block; }

a { color: inherit; text-decoration: none; }

/* ---------- Typography ---------- */
h1, h2, h3 { font-weight: 700; line-height: 1.2; color: var(--heading); }

h1 { font-size: clamp(2.2rem, 5vw, 3.75rem); font-weight: 800; }

h2 { font-size: clamp(1.7rem, 3.5vw, 2.3rem); }

h3 { font-size: 1.1rem; margin-bottom: 0.35rem; }

p { margin-bottom: 0.85rem; }

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

/* ---------- Layout ---------- */
.section-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 3.5rem 1.5rem;
}

.section-header {
  text-align: center;
  max-width: 620px;
  margin: 0 auto 2.5rem;
}

.section-header p {
  font-size: 1rem;
  color: var(--body);
  margin-top: 0.6rem;
}

/* ---------- Buttons ---------- */
.btn-primary, .btn-accent, .btn-outline {
  display: inline-block;
  padding: 0.8rem 1.7rem;
  font-weight: 600;
  font-size: 0.95rem;
  font-family: var(--font);
  border-radius: 8px;
  border: 2px solid transparent;
  cursor: pointer;
  transition: background-color 0.2s, border-color 0.2s, transform 0.15s;
  text-decoration: none;
}

.btn-primary { background-color: var(--primary-dark); color: var(--white); }

.btn-primary:hover { background-color: var(--primary-darker); transform: translateY(-1px); }

.btn-accent { background-color: var(--accent); color: var(--heading); }

.btn-accent:hover { background-color: var(--accent-dark); transform: translateY(-1px); }

.btn-outline {
  background-color: transparent;
  color: var(--heading);
  border-color: rgba(15, 23, 42, 0.35);
}

.btn-outline:hover {
  background-color: rgba(15, 23, 42, 0.06);
  border-color: var(--heading);
}

/* ---------- Nav ---------- */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  background: var(--nav-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  transition: box-shadow 0.3s;
}

.nav.scrolled { box-shadow: 0 2px 18px rgba(0, 0, 0, 0.09); }

.nav-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1.5rem;
  height: 64px;
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

@property --angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.nav-logo {
  --angle: 0deg;
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--heading);
  letter-spacing: -0.02em;
  white-space: nowrap;
  padding: 0.15rem 0.5rem;
  border-radius: 8px;
  border: 4px solid transparent;
  background:
    linear-gradient(rgba(255, 255, 255, 0.97), rgba(255, 255, 255, 0.97)) padding-box,
    conic-gradient(from var(--angle), #14532d, #16a34a, #86efac, #16a34a, #14532d) border-box;
  animation: spin 3s linear infinite;
}

/* Only the "Dev" half is green. The wrapper span inherits the link colour, so
   the rule is class-scoped rather than a bare descendant selector. */
.nav-logo .logo-dev { color: var(--primary); }

/* Command-prompt chevron ahead of the wordmark. Sized in em so it tracks the
   font-size at every breakpoint and leaves the wordmark's own type metrics
   untouched; --primary-darker on the light navbar, mint on the dark footer
   where --primary-darker would disappear into the background. */
.logo-chevron {
  width: 0.5em;
  height: 0.62em;
  flex-shrink: 0;
  display: block;
  margin-top: 0.03em;
  stroke: var(--primary-darker);
  stroke-width: 3.2;
  stroke-linecap: square;
  stroke-linejoin: miter;
  fill: none;
}

@keyframes spin {
  to { --angle: 360deg; }
}

/* Footer wordmark: same lockup as the navbar, reversed onto the footer's dark
   surface so the animated border reads against #0f172a. */
.footer-logo {
  --angle: 0deg;
  border: 4px solid transparent;
  border-radius: 8px;
  padding: 0.15rem 0.5rem;
  background:
    linear-gradient(var(--footer-bg), var(--footer-bg)) padding-box,
    conic-gradient(from var(--angle), #14532d, #16a34a, #86efac, #16a34a, #14532d) border-box;
  animation: spin 3s linear infinite;
}

.footer-logo .logo-chevron { stroke: var(--footer-chevron); }

@media (prefers-reduced-motion: reduce) {
  .nav-logo,
  .footer-logo { animation: none; }
}

/* Login icon: primary green (same as the logo "Dev" text) at rest. On hover a
   visible light-green tint of the primary — the icon stays green, so a --primary-light
   background would be invisible; this keeps the hover noticeable. */
.nav-login:hover { background-color: rgba(22, 163, 74, 0.18); }  /* --primary @ 18% */

.nav-links { display: flex; list-style: none; gap: 0.1rem; margin-left: auto; }

.nav-links a {
  display: block;
  padding: 0.4rem 0.8rem;
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--body);
  border-radius: 6px;
  transition: color 0.2s, background-color 0.2s;
}

.nav-links a:hover { color: var(--primary); background-color: var(--primary-light); }

.nav-mobile-cta { display: none; }

.nav-cta {
  font-size: 0.88rem;
  padding: 0.5rem 1.1rem;
  white-space: nowrap;
  flex-shrink: 0;
  border: 2px solid var(--accent);
}

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  margin-left: auto;
}

.nav-toggle span {
  display: block;
  width: 24px; height: 2px;
  background-color: var(--heading);
  border-radius: 2px;
  transition: transform 0.3s, opacity 0.3s;
}

/* ---------- Anchor Offset ---------- */
#services, #pricing, #process, #contact {
  scroll-margin-top: 50px;
}

/* ---------- Utility Menu ---------- */
.util-menu {
  position: relative;
  flex-shrink: 0;
  margin-left: auto;
}

.util-toggle {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  background: none;
  border: 1.5px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  padding: 8px 10px;
  transition: border-color 0.2s, background-color 0.2s;
}

.util-toggle:hover {
  border-color: var(--primary);
  background-color: var(--primary-light);
}

.util-toggle span {
  display: block;
  width: 20px;
  height: 2px;
  background-color: var(--heading);
  border-radius: 2px;
}

.util-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  min-width: 140px;
  overflow: hidden;
  z-index: 200;
}

.util-dropdown.open { display: block; }

.util-item {
  display: block;
  padding: 0.75rem 1.1rem;
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--body);
  transition: background-color 0.15s, color 0.15s;
}

.util-item:hover {
  background-color: var(--primary-light);
  color: var(--primary);
}

/* ---------- Hero ---------- */
/* The hero ink follows the theme. Dark is the default, so :root carries the
   night values and data-theme="light" reassigns them, matching how every
   other flipping surface is declared above. The illustration itself is NOT a
   token: it cross-fades between two stacked layers further down, because
   background-image does not interpolate.

   The dark-theme ink has to be light: it sits on the night illustration, where
   the original --heading #0f172a measures 1.19:1 and is unreadable. On the
   night sky rgb(22,48,92) the replacements measure 13.03:1 and 9.28:1. Light
   theme keeps the original three colours unchanged, because the daytime
   illustration is bright and dark ink is correct on it. */
:root {
  --hero-heading: var(--white);
  /* Mint. Same value as --on-forest-icon and --footer-chevron, pinned as a
     literal because those tokens flip under data-theme="light". */
  --hero-tagline: #86efac;
  --hero-body:    #86efac;
}
:root[data-theme="light"] {
  --hero-heading: var(--heading);
  --hero-tagline: var(--primary-dark);
  --hero-body:    var(--body);
}

/* background-image cannot be transitioned — there is no interpolation between
   two url() values, so the swap is always instant. The cross-fade is done by
   stacking both illustrations and animating opacity instead: the daytime one
   is the element's own background, the night one is an overlay that fades out
   when data-theme="light" is set. Both carry the same tint so neither state
   doubles it. The cost is that both files are fetched on every visit, which a
   cross-fade cannot avoid. */
.hero {
  position: relative;
  isolation: isolate;
  background:
    /* Matches --forest-tint (#376f57 = 55 111 87). Kept as rgba because the
       gradient needs alpha; update both together if the token changes. */
    radial-gradient(ellipse at center, rgba(55, 111, 87, 0.3) 0%, rgba(55, 111, 87, 0.15) 45%, rgba(55, 111, 87, 0.02) 100%),
    url("../images/hero-illustration.png?v=2") bottom center / cover no-repeat;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--heading);
  padding-top: 64px;
  padding-bottom: 54vh;
}

.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse at center, rgba(55, 111, 87, 0.3) 0%, rgba(55, 111, 87, 0.15) 45%, rgba(55, 111, 87, 0.02) 100%),
    /* ?v=2 — the file name is unchanged, so a returning browser would keep the
       previous night hero cached and never see the corrected door signs. */
    url("../images/hero-illustration-night.png?v=3") bottom center / cover no-repeat;
  opacity: 1;                    /* dark is the default theme */
  transition: opacity 600ms ease;
}

:root[data-theme="light"] .hero::before { opacity: 0; }

/* The hero ink is a colour, which does interpolate, so it can cross-fade on
   the same clock as the illustration underneath it. */
.hero h1,
.hero-tagline,
.hero p { transition: color 600ms ease; }

@media (prefers-reduced-motion: reduce) {
  .hero::before,
  .hero h1,
  .hero-tagline,
  .hero p { transition: none; }
}

/* Above the overlay. Without this the copy sits under the night layer. */
.hero-content { position: relative; z-index: 1; max-width: 760px; padding: 2rem 1.5rem; }

.hero h1 { color: var(--hero-heading); margin-bottom: 0.25rem; letter-spacing: -0.025em; }

.hero-tagline { font-size: clamp(1.3rem, 2.8vw, 1.9rem); font-weight: 600; color: var(--hero-tagline); margin-bottom: 1.25rem; letter-spacing: -0.01em; }

.hero p {
  font-size: clamp(1rem, 2vw, 1.18rem);
  color: var(--hero-body);
  margin-bottom: 2rem;
  line-height: 1.75;
}

.hero-actions { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }

/* ---------- Services ---------- */
.services {
  background-color: var(--forest-tint);
  min-height: 100vh;
  display: flex;
  align-items: center;
}

.services .section-inner { width: 100%; padding-top: 2rem; padding-bottom: 2rem; }

.services-divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 1.75rem 0;
}

.service-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.25rem;
}

.service-card {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  background-color: var(--surface);
  padding: 1.75rem 1.25rem;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  transition: transform 0.2s, box-shadow 0.2s;
}

.service-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }

.service-icon {
  flex-shrink: 0;
  width: 80px;
  height: 80px;
  background: linear-gradient(135deg, var(--primary-light) 0%, var(--primary-tint) 100%);
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--primary);
}

.service-icon svg { width: 38px; height: 38px; }

.service-card h3 { margin-bottom: 0.35rem; }

.service-card p { color: var(--body); font-size: 0.9rem; margin: 0; }

/* ---------- Why (inside services section) ---------- */
.why-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
}

.why-card {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1.25rem;
  border-radius: var(--radius);
  border: 1.5px solid var(--border);
  background-color: var(--surface);
  transition: border-color 0.2s, box-shadow 0.2s;
}

.why-card:hover {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-light);
}

.why-icon {
  flex-shrink: 0;
  width: 80px;
  height: 80px;
  background: linear-gradient(135deg, var(--primary-light) 0%, var(--primary-tint) 100%);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--primary);
}

.why-icon svg { width: 36px; height: 36px; }

.why-card h3 { font-size: 1rem; margin-bottom: 0.25rem; }

.why-card p { font-size: 0.88rem; color: var(--body); margin: 0; }

/* ---------- Pricing ---------- */
.pricing { background-color: var(--forest-tint); }

.pricing .section-inner { padding-top: 1.8rem; padding-bottom: 1.8rem; }

/* stretch (not center) so all three cards share one height regardless of how
   many features or CTAs each one carries */
.pricing-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.75rem;
  align-items: stretch;
}

.pricing-card {
  background-color: var(--surface);
  border-radius: var(--radius);
  padding: 1.35rem 1.75rem;
  box-shadow: var(--shadow);
  position: relative;
  transition: transform 0.2s, box-shadow 0.2s;
  /* column flex + a growing feature list pins every card's CTAs to the same
     baseline, so equal-height cards don't leave buttons floating mid-card */
  display: flex;
  flex-direction: column;
}

.pricing-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }

/* The featured card is distinguished by its border and badge alone — it is not
   scaled up, so all three cards stay the same size. */
.pricing-featured { border: 2px solid var(--primary); }

.pricing-badge {
  position: absolute;
  top: -13px;
  left: 50%;
  transform: translateX(-50%);
  background-color: var(--primary-dark);
  color: var(--white);
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.28rem 0.9rem;
  border-radius: 100px;
  white-space: nowrap;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.pricing-top {
  text-align: center;
  margin-bottom: 0.9rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--border);
}

.pricing-top h3 { font-size: 1.2rem; margin-bottom: 0.65rem; }

.price {
  font-size: 2.8rem;
  font-weight: 800;
  color: var(--primary);
  line-height: 1;
  margin-bottom: 0.2rem;
  letter-spacing: -0.03em;
}

.price-sub { font-size: 0.82rem; color: var(--body); margin: 0; }

.pricing-features { list-style: none; margin-bottom: 1.1rem; flex: 1; }

.pricing-features li {
  padding: 0.3rem 0;
  font-size: 0.9rem;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.pricing-features li::before {
  content: '';
  display: block;
  width: 7px; height: 7px;
  background-color: var(--primary);
  border-radius: 50%;
  flex-shrink: 0;
}

.pricing-features li:last-child { border-bottom: none; }

.pricing-card .btn-primary,
.pricing-card .btn-accent,
.pricing-card .btn-outline { width: 100%; text-align: center; display: block; }

.pricing-card .btn-outline { margin-top: 0.6rem; }

/* ---------- Pricing "See Example" dropdown (Scale card) ---------- */
/* One toggle listing several demos, so a tier with more than one example does
   not stack a button per demo. Mirrors the .util-dropdown pattern in the nav. */
.demo-menu { position: relative; margin-top: 0.6rem; }

.pricing-card .demo-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0;
}

.demo-caret {
  width: 7px;
  height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.2s;
  flex-shrink: 0;
}

.demo-toggle[aria-expanded="true"] .demo-caret { transform: translateY(1px) rotate(-135deg); }

.demo-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  z-index: 200;
  text-align: left;
}

.demo-dropdown.open { display: block; }

.demo-item {
  display: block;
  padding: 0.7rem 1.1rem;
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--heading);
  transition: background-color 0.15s, color 0.15s;
}

.demo-item + .demo-item { border-top: 1px solid var(--border); }

.demo-item:hover { background-color: var(--primary-light); color: var(--primary); }

.demo-item span {
  display: block;
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--body);
  margin-top: 0.1rem;
}

.pricing-note {
  max-width: 65ch;
  margin: 1.1rem auto 0;
  text-align: center;
  font-size: 0.87rem;
  color: var(--body);
}

/* ---------- Retainer Callout ---------- */
/* A call to action, so it sits on white in both themes rather than tinting into
   the --forest-tint band behind it. Same treatment as .about-card: the panel
   keeps the standard dark ink whichever theme is active. */
.retainer-callout {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  margin-top: 2rem;
  padding: 1.5rem 2rem;
  background-color: var(--surface);
  border: 1px solid var(--primary);
  border-radius: 12px;
  flex-wrap: wrap;
}

.retainer-icon {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  background-color: var(--primary);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--white);
}

.retainer-icon svg { width: 24px; height: 24px; }

.retainer-body { flex: 1; min-width: 200px; max-width: 65ch; }

.retainer-body h3 { font-size: 1rem; font-weight: 700; color: var(--heading); margin-bottom: 0.35rem; }

/* Three prices in a row rather than one headline figure. Separation is spacing
   rather than a divider rule: the row wraps on narrow screens, and any border
   or bullet between items strands itself at the wrap point. */
.retainer-tiers {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.75rem;
  list-style: none;
  margin: 0 0 0.6rem;
  padding: 0;
}

.retainer-tiers li {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  font-size: 0.87rem;
}

.tier-name { font-weight: 600; color: var(--heading); }

.tier-price { font-weight: 700; color: var(--primary-dark); }

.tier-per { font-weight: 500; font-size: 0.8em; color: var(--body); }

.tier-note { color: var(--body); font-size: 0.82rem; }

.retainer-body p { font-size: 0.87rem; color: var(--body); line-height: 1.6; }

.retainer-cta { flex-shrink: 0; white-space: nowrap; }

/* ---------- Process ---------- */
.process { background-color: var(--forest-tint); }

.process-steps {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.25rem;
}

.process-step {
  background-color: var(--surface);
  border-radius: var(--radius);
  padding: 1.75rem 1.25rem;
  box-shadow: var(--shadow);
  transition: transform 0.2s, box-shadow 0.2s;
}

.process-step:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }

.step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  background-color: var(--primary-dark);
  color: var(--white);
  border-radius: 50%;
  font-size: 0.95rem;
  font-weight: 800;
  margin-bottom: 1rem;
}

.process-step h3 { font-size: 1.05rem; margin-bottom: 0.45rem; }

.process-step p { font-size: 0.88rem; color: var(--body); margin: 0; }

/* ---------- Testimonials ---------- */
.testimonials { background-color: var(--surface); }

.testimonial-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

.testimonial-card {
  background-color: var(--bg-subtle);
  border-radius: var(--radius);
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.testimonial-stars { color: var(--accent); font-size: 1rem; letter-spacing: 0.15em; }

.testimonial-card > p {
  font-size: 0.9rem;
  font-style: italic;
  color: var(--body);
  flex: 1;
  margin: 0;
  line-height: 1.7;
}

.testimonial-author { display: flex; flex-direction: column; gap: 0.1rem; }

.testimonial-author strong { color: var(--heading); font-size: 0.9rem; }

.testimonial-author span { font-size: 0.82rem; color: var(--body); }

/* ---------- Contact ---------- */
/* Shares the --forest-tint surface with .process above and .contact below, so
   the three read as one band. Its ink therefore flips with the theme too. */
.about { background-color: var(--forest-tint); }

/* Was 860px. Now inherits the 1200px .section-inner width so .about-card's
   edges line up with the pricing cards in the band above. */

/* The white panel that carries the about copy. Sits on --forest-tint like the
   pricing cards do, so its ink stays the normal dark palette in both themes
   rather than flipping with the band behind it. */
.about-card {
  background-color: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 3rem 3.5rem 3.25rem;
}

@media (max-width: 700px) {
  .about-card { padding: 2rem 1.5rem 2.25rem; }
}

.about-lead {
  font-size: 1.05rem;
  line-height: 1.8;
  color: var(--body);
  margin: 0 auto 2rem;
  max-width: 680px;
  text-align: center;
}

.about-lead strong { color: var(--heading); }

.about-commitments {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem 2rem;
  max-width: 720px;
  margin: 0 auto;
  padding: 1.75rem 0 0;
  border-top: 1px solid var(--border);
}

.about-commitment { display: flex; gap: 0.7rem; align-items: flex-start; }

.about-commitment svg { flex-shrink: 0; margin-top: 0.2rem; color: var(--primary); }

.about-commitment p { margin: 0; font-size: 0.95rem; line-height: 1.6; color: var(--body); }

.about-commitment strong { color: var(--heading); display: block; }

@media (max-width: 700px) {
  .about-commitments { grid-template-columns: 1fr; gap: 1.1rem; }
}

.contact { background-color: var(--forest-tint); }

.contact .section-inner { max-width: 760px; }

.contact-email { font-size: 0.93rem; margin-top: 0.4rem; }

.contact-email a {
  /* White on --forest-tint is 5.88:1. Reads as a link against the cream body
     copy through weight and underline rather than hue. Flips to dark green on
     the light theme, where white would vanish. */
  color: var(--on-forest);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Text sitting directly on a section's forest-tint background (headings,
   subtitles, loose paragraphs) — not the white cards inside those sections,
   which keep their normal heading/body colors. */
.services .section-header h2,
.services .section-header p,
.pricing .section-header h2,
.pricing .section-header p,
.pricing-note,
.about .section-header h2,
.about .section-header p,
.process .section-header h2,
.process .section-header p,
.contact .section-header h2,
.contact .section-header p,
.contact-email {
  color: var(--tan-text);
}

.customer-form { margin-top: 2rem; display: grid; gap: 0.9rem; }

.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0.9rem; }

.customer-form input,
.customer-form textarea,
.customer-form select {
  padding: 0.85rem 1rem;
  border-radius: 8px;
  border: 1.5px solid var(--border);
  font-size: 0.93rem;
  font-family: var(--font);
  color: var(--heading);
  background-color: var(--surface);
  transition: border-color 0.2s, box-shadow 0.2s;
  width: 100%;
}

/* Muted slate-400 placeholder ink, pinned by value: the form panel is white in
   both themes, and --footer-text (the only #94a3b8 token) flips dark in light
   mode, which placeholders must not do. The chevron below bakes the same hex
   into its data-URI because var() cannot evaluate inside a URL. */
.customer-form input::placeholder,
.customer-form textarea::placeholder { color: #94a3b8; }

.customer-form select {
  color: #94a3b8;
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9rem center;
  background-size: 16px;
  padding-right: 2.5rem;
}

.customer-form select.has-value { color: var(--heading); }

.customer-form input:focus,
.customer-form textarea:focus,
.customer-form select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-light);
}

.customer-form textarea { min-height: 140px; resize: vertical; }

.customer-form .btn-accent {
  border: none;
  font-size: 1rem;
  padding: 0.95rem 2rem;
  width: 100%;
  display: block;
}

.customer-form .btn-accent:disabled { opacity: 0.65; cursor: not-allowed; transform: none; }

#formStatus {
  font-size: 0.88rem;
  text-align: center;
  padding: 0.55rem;
  border-radius: 6px;
  min-height: 1.4rem;
}

#formStatus.success { color: var(--primary-dark); background-color: var(--primary-light); }

#formStatus.error { color: #dc2626; background-color: #fef2f2; }

/* ---------- Footer ---------- */
/* ---------- Legal pages (privacy.html) ----------------------------------------
   These rules were a <style> block in privacy.html. The page never got them:
   Program.cs sends `style-src 'self'` with no 'unsafe-inline', so Chrome blocked
   the whole block and the page rendered with no measure, no padding and no
   heading scale. Self-hosting them here is what the CSP asks for, and it keeps
   rule 2 at the top of this file - one stylesheet per page.

   The top padding clears the fixed 64px navbar. The old inline value was 3rem,
   which is 48px, so the h1 sat behind the bar even before the CSP blocked it. */
/* The notice is four lines, so it is a card on a tinted ground rather than a
   full-width column. The ground is --forest-tint, the site's own section
   background; the card is --surface, the same surface every other card on the
   site uses. Both already flip with the theme, so nothing new is introduced. */
.legal {
  background: var(--forest-tint);
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: calc(64px + 3rem) 1.5rem 4rem;
}

.legal-card {
  width: 100%;
  max-width: 34rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 2.5rem 2.25rem;
}

.legal h1 { font-size: 1.9rem; margin: 0 0 0.3rem; }

.legal h2 { font-size: 1.15rem; margin: 2rem 0 0.5rem; }

.legal .updated { font-size: 0.85rem; margin: 0 0 1.75rem; }

.legal p, .legal li { color: var(--body); line-height: 1.7; }

.legal p + p { margin-top: 1.1rem; }

/* The global reset zeroes padding, so a legal list needs its marker and indent
   restored explicitly. */
.legal ul { list-style: disc; padding-left: 1.2rem; margin: 0.35rem 0 0; }

.legal li + li { margin-top: 0.2rem; }

/* `a { color: inherit; text-decoration: none }` is the base rule, so without
   this every link on the page is indistinguishable from the sentence it sits in.
   --primary-dark is what the blocked inline block already specified. */
.legal a { color: var(--primary-dark); text-decoration: underline; }

.legal a:hover { color: var(--primary-darker); }

.footer { background-color: var(--footer-bg); color: var(--footer-text); }

.footer-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 3.5rem 1.5rem 3rem;
  display: grid;
  /* ONE column, because this footer has one child. The three-column rule came
     from LogansDevWebsite, whose footer really does carry Quick Links and Legal
     columns. Grid lays out the gap between empty tracks too, and justify-content
     centers the tracks AND their gaps - so with three tracks and one child,
     2 x 6rem of empty gap sat to the right and pushed the brand block 96px left
     of center. */
  grid-template-columns: auto;
  justify-content: center;
  gap: 2.5rem 6rem;
}

.footer-logo {
  font-size: 1.4rem;
  font-weight: 800;
  color: var(--on-footer-strong);
  letter-spacing: -0.02em;
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
  white-space: nowrap;
  margin-bottom: 0.85rem;
}

.footer-logo .logo-dev { color: var(--footer-dev); }

/* This footer has one column and it is centered, so its text is centered too.
   Left-aligned text inside a centered block reads as a block that missed its
   mark, and .footer-bottom directly below is already centered - two competing
   axes in a four-line footer. */
.footer-col { text-align: center; }

.footer-col p { font-size: 0.88rem; line-height: 1.75; color: var(--footer-text); }

.footer-col .footer-contact { margin-top: 0.9rem; }

/* The 34ch measure still applies; without the auto margins the paragraph box
   would sit against the left edge of the column and undo the centering. */
.footer-col > p:first-of-type { max-width: 34ch; margin-inline: auto; }

.footer-col h4 {
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--footer-text);
  margin-bottom: 1rem;
}

.footer-col ul { list-style: none; display: flex; flex-direction: column; gap: 0.55rem; }

.footer-col ul a { font-size: 0.88rem; color: var(--footer-text); transition: color 0.2s; }

.footer-col ul a:hover { color: var(--on-footer-strong); }

.footer-col a[href^="mailto:"] { color: var(--footer-link); font-size: 0.9rem; transition: color 0.2s; }

.footer-col a[href^="mailto:"]:hover { color: var(--footer-link-hover); }

.footer-bottom {
  border-top: 1px solid var(--footer-divider);
  padding: 1.25rem;
  text-align: center;
  font-size: 0.82rem;
  color: var(--footer-text);
}

/* The base rule is `a { color: inherit; text-decoration: none }`, so a link on
   this line would be indistinguishable from the copyright text beside it. The
   underline is what marks it, and the hover reuses the same pair the footer
   link columns already use - no new color enters the palette. */
.footer-bottom a { text-decoration: underline; transition: color 0.2s; }

.footer-bottom a:hover { color: var(--on-footer-strong); }

.footer-bottom .footer-sep { opacity: 0.5; margin: 0 0.15rem; }

/* ---------- Responsive ---------- */
@media (max-width: 1024px) {
  .service-grid { grid-template-columns: repeat(2, 1fr); }
  .pricing-grid { grid-template-columns: 1fr; max-width: 440px; margin: 0 auto; }
  /* Stacked single column — lead with the recommended tier */
  .pricing-featured { order: -1; }
  .process-steps { grid-template-columns: repeat(2, 1fr); }
  /* Content-sized and packed left, matching the desktop treatment — fr units
     here spread the three link columns across the full width. */
  .footer-inner { grid-template-columns: repeat(2, auto); justify-content: center; }
  .footer-col:first-child { grid-column: 1 / -1; }
}

@media (max-width: 768px) {
  .nav-toggle { display: flex; }
  .nav-links, .nav-cta { display: none; }
  .nav.nav-open .nav-links {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 64px; left: 0; right: 0;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: 0.6rem 1rem;
    gap: 0.1rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
  }
  .nav-mobile-cta { display: block; }
  .nav-mobile-cta a {
    background-color: var(--accent);
    color: var(--heading);
    font-weight: 600;
    text-align: center;
    border-radius: 8px;
    margin-top: 0.25rem;
  }
  .nav-mobile-cta a:hover { background-color: var(--accent-dark); color: var(--heading); }
  .why-grid { grid-template-columns: 1fr; }
  .testimonial-grid { grid-template-columns: 1fr; }
  .form-row { grid-template-columns: 1fr; }
  .footer-inner { grid-template-columns: 1fr; gap: 1.75rem; padding: 2.75rem 1.5rem 2.25rem; }
  .footer-col:first-child { grid-column: auto; }
}

@media (max-width: 600px) {
  .service-grid { grid-template-columns: 1fr; }
  .process-steps { grid-template-columns: 1fr; }
  .hero-actions { flex-direction: column; align-items: center; }
  .hero-actions a { width: 100%; max-width: 300px; text-align: center; }
}


/* ══════════════════════════════════════════════════════════════════
   PORTAL — portal.html, login.html, register.html, credentials.html
   ══════════════════════════════════════════════════════════════════ */


/* Hanken Grotesk used to be pulled from Google here. It is now self-hosted and
   declared in css/fonts.css, which every page loads before this file. */

/* ============================================================
   Shared modal / auth-card styles — SINGLE SOURCE OF TRUTH
   Used by: login.html, register.html, portal.html (auth gate)
   All modal class names use the .modal-* namespace.
   ============================================================ */

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

/* Full-screen gradient backdrop that centers the card.
   position:fixed so it overlays the portal dashboard when shown. */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: var(--forest-tint);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  overflow-y: auto;
  z-index: 9999;
}

.modal-card {
  background: #f0fdf4;
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  width: 100%;
  max-width: 400px;
  padding: 40px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  /* Every modal/state renders at the height of the tallest card
     (portal registration state) so cards never resize or differ. */
  min-height: 660px;
  display: flex;
  flex-direction: column;
}

/* Light theme: white card on a mint backdrop. --forest-tint flips to the pale
   #eef7f1 in light mode, which read as neither mint nor a deliberate surface,
   and the card's #f0fdf4 was near-invisible against it. */
:root[data-theme="light"] .modal-overlay { background: var(--primary-tint); }

:root[data-theme="light"] .modal-card { background: var(--white); }

/* ---------- Header ---------- */
.page-portal .modal-header {
  text-align: center;
  margin-bottom: 40px;
}

.modal-logo {
  --angle: 0deg;
  display: inline-block;
  font-family: 'Hanken Grotesk', Arial, Helvetica, sans-serif;
  font-size: 1.5rem;
  font-weight: 800;
  color: #0f172a;
  letter-spacing: -0.02em;
  white-space: nowrap;
  padding: 0.15rem 0.5rem;
  border-radius: 8px;
  border: 4px solid transparent;
  background:
    linear-gradient(rgba(255, 255, 255, 0.97), rgba(255, 255, 255, 0.97)) padding-box,
    conic-gradient(from var(--angle), #14532d, #16a34a, #86efac, #16a34a, #14532d) border-box;
  animation: logo-spin 3s linear infinite;
  margin-bottom: 16px;
  text-decoration: none;
}

.modal-logo span { color: #16a34a; }

@keyframes logo-spin {
  to { --angle: 360deg; }
}

@media (prefers-reduced-motion: reduce) {
  .modal-logo, .brand-logo { animation: none; }
}

.modal-header h1 {
  font-size: 24px;
  font-weight: 700;
  color: #0f172a;
  margin-bottom: 6px;
}

.modal-header p {
  color: #475569;
  font-size: 14px;
}

/* ---------- Form fields ---------- */
.modal-form-group {
  margin-bottom: 20px;
}

.modal-form-group label {
  display: block;
  margin-bottom: 8px;
  color: #0f172a;
  font-weight: 600;
  font-size: 14px;
}

.modal-form-group input {
  width: 100%;
  padding: 12px;
  border: 2px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  color: #0f172a;
  background: var(--surface);
  transition: border-color 0.2s, box-shadow 0.2s;
}

.modal-form-group input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

.modal-form-group small {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: #94a3b8;
}

/* ---------- Primary button ---------- */
.modal-button {
  width: 100%;
  padding: 12px;
  background: linear-gradient(135deg, #16a34a 0%, #15803d 100%);
  color: var(--white);
  border: none;
  border-radius: 8px;
  font-size: 16px;
  font-weight: bold;
  font-family: inherit;
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
  margin-top: 10px;
}

.modal-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 20px rgba(22, 163, 74, 0.3);  /* --primary @ 30% */
}

.modal-button:active { transform: translateY(0); }

.modal-button:disabled {
  opacity: 0.7;
  cursor: not-allowed;
  transform: none;
}

/* ---------- Messages ---------- */
.modal-error {
  background: #fef2f2;
  border: 1px solid #fecaca;
  color: #dc2626;
  padding: 12px;
  border-radius: 8px;
  margin-bottom: 20px;
  font-size: 14px;
  display: none;
}

.modal-error.show { display: block; }

.modal-success {
  background: var(--primary-tint);
  border: 1px solid #86efac;
  color: var(--primary-dark);
  padding: 12px;
  border-radius: 8px;
  margin-bottom: 20px;
  font-size: 14px;
  display: none;
}

.modal-success.show { display: block; }

.modal-loading {
  display: none;
  text-align: center;
  color: var(--body);
  font-size: 14px;
  margin-top: 10px;
}

.modal-loading.show { display: block; }

/* ---------- Footer + inline helper links ---------- */
/* One class for every centered helper-link row beneath a form:
   login's "New user"/"Manage credentials" and the portal's
   "Already have an account? Sign in" toggle. */
.modal-link {
  text-align: center;
  margin-top: 16px;
  font-size: 13px;
  color: var(--body);
}

.modal-link a {
  color: var(--primary-dark);
  text-decoration: none;
  font-weight: 500;
}

.modal-link a:hover { text-decoration: underline; }

.modal-footer {
  text-align: center;
  margin-top: auto;          /* pin to the bottom of the fixed-height card */
  padding-top: 20px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  color: var(--footer-text);
}

.modal-footer a {
  color: var(--primary-dark);
  text-decoration: none;
}

.modal-footer a:hover { text-decoration: underline; }

/* ============================================================
   Multi-state auth gate (portal.html) — portal-only modal bits
   ============================================================ */
/* Form state toggling: hidden by default, .active shows it */
.modal-form { display: none; }

.modal-form.active { display: block; }

.modal-welcome {
  text-align: center;
  margin-bottom: 18px;
  font-weight: 600;
  color: #0f172a;
}

/* ---------- OTP 6-box access code entry ---------- */
.modal-otp-wrapper {
  margin-bottom: 20px;
}

.modal-otp-label {
  display: block;
  text-align: center;
  margin-bottom: 14px;
  color: #0f172a;
  font-weight: 600;
  font-size: 14px;
}

.modal-otp-group {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.modal-otp-box {
  width: 36px;
  height: 38px;
  padding: 0;
  border: 2px solid var(--border);
  border-radius: 4px;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  color: #0f172a;
  background: var(--surface);
  font-family: 'Hanken Grotesk', monospace;
  caret-color: transparent;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
  line-height: 1;
  box-sizing: border-box;
}

.modal-otp-box:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

.modal-otp-box.filled {
  border-color: var(--primary);
  background: var(--primary-tint);
}

.modal-otp-hint {
  display: block;
  text-align: center;
  margin-top: 10px;
  font-size: 12px;
  color: #94a3b8;
}

@media (max-width: 400px) {
  .modal-otp-group { gap: 6px; }
}

body.page-portal {
  font-family: 'Hanken Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--forest-tint);
  color: var(--heading);
  min-height: 100vh;
}

/* Dashboard settings form fields (distinct from .modal-form-group used in auth gate) */
.page-portal .form-group {
  margin-bottom: 20px;
}

.page-portal .form-group label {
  display: block;
  margin-bottom: 8px;
  color: var(--heading);
  font-weight: 600;
  font-size: 14px;
}

.form-group input,
.form-group textarea,
.form-group select {
  width: 100%;
  padding: 11px 13px;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  color: var(--heading);
  background: var(--surface);
  transition: border-color 0.2s, box-shadow 0.2s;
}

.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

.form-group small {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--heading);
}

/* Dashboard utility classes (toggled by JS) */
.portal-hidden { display: none; }

.page-portal .empty-state {
  color: var(--heading);
  font-size: 14px;
}

/* ==================== PORTAL HEADER ==================== */
.portal-header {
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: 0 2px 18px rgba(0, 0, 0, 0.09);
}

.header-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 16px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.header-brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.brand-logo {
  --angle: 0deg;
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--heading);
  padding: 0.15rem 0.5rem;
  border-radius: 8px;
  border: 4px solid transparent;
  background:
    linear-gradient(rgba(255, 255, 255, 0.97), rgba(255, 255, 255, 0.97)) padding-box,
    conic-gradient(from var(--angle), #14532d, #16a34a, #86efac, #16a34a, #14532d) border-box;
  animation: logo-spin 3s linear infinite;
}

.brand-logo span { color: var(--primary); }

.header-divider {
  color: var(--border);
  font-size: 18px;
  font-weight: 300;
}

.header-subtitle {
  color: var(--heading);
  font-size: 0.92rem;
  font-weight: 500;
}

.header-right {
  display: flex;
  align-items: center;
  gap: 16px;
}

.client-name {
  font-size: 14px;
  color: var(--heading);
  font-weight: 500;
}

.btn-logout {
  padding: 7px 14px;
  background: transparent;
  border: 2px solid #f59e0b;
  color: var(--primary);
  border-radius: 8px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s;
}

.btn-logout:hover {
  border-color: #d97706;
  color: #d97706;
}

/* ==================== TAB NAV ==================== */
.portal-tabs {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.tabs-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 28px;
  display: flex;
}

.tab-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 15px 20px;
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--body);
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s;
  margin-bottom: -1px;
  white-space: nowrap;
}

.tab-btn:hover { color: var(--primary); }

.tab-btn.active {
  color: var(--primary);
  border-bottom-color: var(--primary);
}

.tab-btn svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

/* ==================== PORTAL CONTENT ==================== */
.portal-main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 32px 28px 60px;
}

.tab-panel { display: none; }

.tab-panel.visible { display: block; }

.panel-heading {
  margin-bottom: 24px;
}

.panel-heading h2 {
  font-size: 22px;
  font-weight: 700;
  margin-bottom: 4px;
  color: var(--tan-text);
}

.panel-heading p {
  font-size: 14px;
  color: var(--tan-text);
}

.card-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.portal-card {
  background: var(--surface);
  border-radius: 12px;
  box-shadow: var(--shadow);
  padding: 26px;
  border: 1px solid var(--border);
}

.card-title {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--heading);
  margin-bottom: 18px;
  display: flex;
  align-items: center;
  gap: 7px;
}

.card-title svg {
  width: 13px;
  height: 13px;
}

/* Welcome card
   Recolored 2026-08-16, layout untouched. The --primary-dark -> --primary
   gradient is gone; each theme gets one flat surface from the home palette.
   DARK  light mint --primary-tint #dcfce7, ink --primary-darker (11.9:1).
         Distinct from the --surface #f0fdf4 cards around it, and the light
         counterpart of the light theme's dark green below.
   LIGHT forest green #376f57 — the home page's dark band color — ink white
         (5.88:1). Hardcoded because --forest-tint flips to #eef7f1 under
         data-theme="light"; every text color below has an explicit light
         override, so no flipping token leaks through. */
.welcome-card {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  background: var(--primary-tint);
  color: var(--primary-darker);
  border: none;
}

.welcome-card .card-title { color: var(--primary-dark); opacity: 1; }

.welcome-text h3 {
  font-size: 21px;
  font-weight: 700;
  margin-bottom: 5px;
  color: var(--primary-darker);
}

.welcome-text p {
  font-size: 14px;
  opacity: 1;
  color: var(--primary-dark);
}

.site-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 14px;
  color: var(--primary-dark);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  background: var(--white);
  padding: 7px 14px;
  border-radius: 20px;
  transition: background 0.2s;
}

.site-link:hover {
  background: var(--primary-dark);
  color: var(--white);
}

/* Light theme flips the card to the forest green, all type white (5.88:1). */
:root[data-theme="light"] .welcome-card {
  background: #376f57;  /* home page dark band; --forest-tint flips here */
  color: var(--white);
}

:root[data-theme="light"] .welcome-card .card-title { color: var(--white); opacity: 0.75; }

:root[data-theme="light"] .welcome-text h3 { color: var(--white); }

:root[data-theme="light"] .welcome-text p { color: var(--white); }

:root[data-theme="light"] .site-link {
  color: var(--white);
  background: rgba(255, 255, 255, 0.15);
}

:root[data-theme="light"] .site-link:hover {
  background: var(--white);
  color: var(--primary-darker);
}

.welcome-icon {
  font-size: 52px;
  flex-shrink: 0;
}

/* Info rows */
.info-row {
  display: flex;
  flex-direction: column;
}

.info-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}

.info-item:last-child { border-bottom: none; padding-bottom: 0; }

.info-item:first-child { padding-top: 0; }

.info-label {
  color: var(--heading);
  font-weight: 500;
  flex-shrink: 0;
  min-width: 120px;
}

.info-value {
  color: var(--heading);
  font-weight: 500;
  text-align: right;
  word-break: break-all;
}

.info-value a {
  color: var(--primary);
  text-decoration: none;
}

.info-value a:hover { text-decoration: underline; }

/* Status card */
.status-main-card { grid-column: 1 / -1; }

.status-hero {
  display: flex;
  align-items: center;
  gap: 20px;
}

.status-icon {
  font-size: 38px;
  width: 68px;
  height: 68px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  flex-shrink: 0;
}

.status-icon.down { background: #fecaca; }

.status-icon.live { background: #dcfce7; }

.status-icon.pending { background: #fef9c3; }

.status-icon.issue { background: #fee2e2; }

.status-details h3 {
  font-size: 20px;
  font-weight: 700;
  margin-bottom: 4px;
}

.status-details p {
  font-size: 13px;
  color: var(--heading);
  margin-top: 2px;
}

.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  margin-top: 8px;
}

.status-badge::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  display: inline-block;
}

.status-badge.down { background: #fecaca; color: #7f1d1d; }

.status-badge.down::before { background: #dc2626; }

.status-badge.live { background: #dcfce7; color: #166534; }

.status-badge.live::before { background: #22c55e; }

.status-badge.pending { background: #fef9c3; color: #854d0e; }

.status-badge.pending::before { background: #eab308; }

.status-badge.issue { background: #fee2e2; color: #991b1b; }

.status-badge.issue::before { background: #ef4444; }

/* Links list */
.links-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.link-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 13px;
  background: var(--subtle);
  border: 1px solid var(--border);
  border-radius: 8px;
  text-decoration: none;
  color: var(--heading);
  font-size: 14px;
  font-weight: 500;
  transition: border-color 0.2s, background 0.2s, color 0.2s;
}

.link-row:hover {
  border-color: var(--primary);
  background: var(--primary-light);
  color: var(--primary-dark);
}

.link-row-label { flex: 1; }

.link-arrow { color: #cbd5e1; font-size: 12px; }

/* Support list */
.support-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.support-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.support-icon-wrap {
  width: 38px;
  height: 38px;
  border-radius: 8px;
  background: var(--primary-light);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  flex-shrink: 0;
}

.support-row-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--heading);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 2px;
}

.support-row-value {
  font-size: 14px;
  color: var(--heading);
  font-weight: 500;
}

.support-row-value a {
  color: var(--primary);
  text-decoration: none;
}

.support-row-value a:hover { text-decoration: underline; }

/* ==================== SETTINGS ==================== */
.settings-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.full-col { grid-column: 1 / -1; }

.settings-form .form-group input:focus,
.settings-form .form-group textarea:focus,
.settings-form .form-group select:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

.settings-form .form-group textarea {
  resize: vertical;
  min-height: 96px;
}

.page-portal .btn-primary, .page-admin .btn-primary {
  padding: 10px 22px;
  background: var(--primary);
  color: var(--white);
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.2s, transform 0.15s;
}

.page-portal .btn-primary:hover, .page-admin .btn-primary:hover {
  background: var(--primary-dark);
  transform: translateY(-1px);
}

.btn-primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}

.form-note {
  margin-top: 10px;
  font-size: 13px;
  display: none;
}

.form-note.show { display: block; }

.form-note.ok { color: var(--primary-dark); }

.form-note.err { color: #be123c; }

/* Document list */
.doc-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.doc-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--subtle);
}

.doc-name {
  font-size: 14px;
  font-weight: 500;
  color: var(--heading);
}

.doc-meta {
  font-size: 12px;
  color: var(--heading);
  margin-top: 2px;
}

.btn-dl {
  padding: 7px 14px;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  color: var(--heading);
  text-decoration: none;
  transition: border-color 0.2s, color 0.2s;
  flex-shrink: 0;
}

.btn-dl:hover {
  border-color: var(--primary);
  color: var(--primary);
}

/* ==================== LOADING ==================== */
.portal-loading {
  text-align: center;
  padding: 60px 20px;
  color: var(--tan-text);
  font-size: 15px;
}

.spinner {
  width: 30px;
  height: 30px;
  border: 3px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: spinner-rotate 0.75s linear infinite;
  margin: 0 auto 14px;
}

/* Renamed from `spin`: index.css already defines a different @keyframes spin
   for the nav logo's conic-gradient border. Keyframe names are global. */

@keyframes spinner-rotate { to { transform: rotate(360deg); } }

/* ==================== FOOTER ==================== */
.portal-footer {
  text-align: center;
  padding: 22px;
  font-size: 13px;
  color: var(--heading);
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.portal-footer a {
  color: var(--primary);
  text-decoration: none;
}

/* ============================================================
   Credentials page — two-state modal (login → management)
   ============================================================ */
/* Wide card for the authenticated credential-management state */
.modal-card.wide {
  max-width: 800px; /* exactly double the 400px single-modal width */
  min-height: unset; /* auth state sizes to content; unauth state keeps 660px */
}

/* Footer auth state: margin-top lifts border off the button; padding shifts link lower */
.modal-card.wide .modal-footer {
  margin-top: 16px;
  padding: 12px 0 2px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Auth-state header: LogansDev | Manage Credentials on one line */
.creds-header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  text-align: left;
  margin-bottom: 28px;
}

.creds-header .modal-logo { margin-bottom: 0; }

.creds-header h1 { margin-bottom: 0; font-size: 20px; }

/* Pipe separator — hidden in unauth state, visible in auth state */
.creds-pipe { display: none; }

.creds-header .creds-pipe {
  display: inline;
  color: #cbd5e1;
  font-weight: 300;
  font-size: 22px;
  flex-shrink: 0;
}

/* Two-column credential management layout */
.creds-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  flex: 1;
}

.creds-col:first-child {
  padding-right: 32px;
  border-right: 1px solid var(--border);
}

.creds-col:last-child {
  padding-left: 32px;
}

@media (max-width: 640px) {
  .creds-columns { grid-template-columns: 1fr; }
  .creds-col:first-child {
    padding-right: 0;
    border-right: none;
    border-bottom: 1px solid var(--border);
    padding-bottom: 28px;
    margin-bottom: 28px;
  }
  .creds-col:last-child { padding-left: 0; }
}

/* ==================== RESPONSIVE ==================== */
@media (max-width: 768px) {
  .header-inner { padding: 14px 16px; }
  .header-subtitle,
  .client-name { display: none; }
  .tabs-inner { padding: 0 8px; overflow-x: auto; }
  .tab-btn { padding: 13px 13px; font-size: 13px; }
  .portal-main { padding: 20px 16px 40px; }
  .card-grid,
  .settings-grid { grid-template-columns: 1fr; }
  .welcome-card { flex-direction: column; align-items: flex-start; }
  .welcome-icon { display: none; }
  .status-hero { flex-direction: column; align-items: flex-start; }
}


/* ══════════════════════════════════════════════════════════════════
   ADMIN — admin.html (internal; excluded from any client-site template)
   ══════════════════════════════════════════════════════════════════ */


body.page-admin {
  /* Was a system stack led by 'Inter', which the site never shipped, so admin
     silently rendered in Segoe UI while every other page used Hanken Grotesk.
     Now the same self-hosted face as the rest of the site. */
  font-family: var(--font);
  background: var(--forest-tint);
  color: var(--heading);
  min-height: 100vh;
}

@media (prefers-reduced-motion: reduce) {
  .brand-logo { animation: none; }
}

/* Heading row: h2 left, search centered on the page (3-col grid keeps the
   middle column centered regardless of the heading's width). */
.panel-heading-row {
  display: grid;
  grid-template-columns: 1fr minmax(220px, 400px) 1fr;
  align-items: center;
  gap: 16px;
}

.panel-heading-row h2 {
  margin-bottom: 0;
  line-height: 30px;         /* fixes the h2 row height the search matches */
  justify-self: start;
}

.panel-heading-balance { justify-self: end; }

.panel-search {
  height: 30px;              /* same height as the h2 line */
  width: 100%;
  padding: 0 14px 0 34px;    /* left room for the magnifier icon */
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-size: 13px;
  font-family: inherit;
  color: var(--heading);
  /* Feather "search" magnifier as an inline data-URI (CSP img-src allows data:).
     The %2394a3b8 stroke is pinned by value: var() cannot evaluate inside a
     URL, and --surface stays light in both themes so the slate icon holds. */
  background: var(--surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E") no-repeat 12px center;
  transition: border-color 0.2s, box-shadow 0.2s;
}

.panel-search::placeholder { color: var(--muted); }

.panel-search:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

.message-count {
  display: inline-block;
  margin-top: 10px;
  background: var(--primary);
  color: var(--white);
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
}

/* ==================== STATES ==================== */
.loading {
  text-align: center;
  color: var(--tan-text);
  font-size: 14px;
  padding: 24px 0;
}

.error {
  background: #fee2e2;
  border: 1px solid #fecaca;
  color: #991b1b;
  padding: 12px 16px;
  border-radius: 8px;
  margin-bottom: 20px;
  font-size: 14px;
}

.page-admin .empty-state {
  text-align: center;
  color: var(--tan-text);
  font-size: 14px;
  padding: 40px 0;
}

.empty-state-icon {
  font-size: 48px;
  margin-bottom: 10px;
}

/* ==================== SUBMISSION PANELS (collapsible accordion) ==================== */
.messages-grid {
  display: grid;
  gap: 12px;
}

.message-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow);
  transition: box-shadow 0.2s;
}

.message-card:hover {
  box-shadow: var(--shadow-lg);
}

/* Collapsed header row — the always-visible summary */
.message-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 22px;
  cursor: pointer;
  list-style: none;                 /* hide default disclosure marker */
  user-select: none;
}

.message-summary::-webkit-details-marker { display: none; }

.message-name {
  font-size: 15px;
  font-weight: 700;
  color: var(--heading);
}

.summary-company {
  font-size: 13px;
  color: var(--heading);
}

.summary-spacer { flex: 1; }

.message-date {
  font-size: 12px;
  color: var(--heading);
  white-space: nowrap;
}

/* CSS chevron — rotates when the panel is open */
.summary-chevron {
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg);        /* pointing right */
  transition: transform 0.2s;
  flex-shrink: 0;
}

.message-card[open] .summary-chevron {
  transform: rotate(45deg);         /* pointing down */
}

.message-card[open] .message-summary {
  border-bottom: 1px solid var(--border);
}

/* Expanded body */
.message-body {
  padding: 16px 22px 20px;
}

.message-details {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 15px;
  margin-bottom: 14px;
}

.detail-field { font-size: 13px; }

.detail-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--heading);
  margin-bottom: 3px;
}

.detail-value {
  color: var(--heading);
  word-break: break-word;
}

.detail-value a {
  color: var(--primary);
  text-decoration: none;
}

.detail-value a:hover { text-decoration: underline; }

.message-text {
  background: var(--subtle);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 16px;
  font-size: 14px;
  color: var(--body);
  line-height: 1.55;
  margin-bottom: 14px;
}

.message-actions {
  display: flex;
  gap: 10px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

/* ==================== BUTTONS ==================== */
.btn {
  padding: 8px 16px;
  border: none;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s, color 0.2s, transform 0.15s;
}

.btn-accept {
  background: var(--primary);
  color: var(--white);
}

.btn-accept:hover { background: var(--primary-dark); }

.btn-decline {
  background: var(--surface);
  border: 1.5px solid #fcd34d;
  color: #854d0e;
}

.btn-decline:hover {
  background: #fef9c3;
  border-color: #f59e0b;
}

.actions-spacer { flex: 1; }

.btn-restore {
  background: var(--surface);
  border: 1.5px solid var(--primary);
  color: var(--primary);
}

.btn-restore:hover {
  background: var(--primary-light);
}

.btn-edit {
  background: var(--surface);
  border: 1.5px solid var(--border);
  color: var(--heading);
}

.btn-edit:hover {
  border-color: var(--primary);
  color: var(--primary);
}

.btn-delete {
  background: var(--danger);
  color: var(--white);
}

.btn-delete:hover { background: var(--danger-dark); }

.btn-save {
  background: var(--primary);
  color: var(--white);
  padding: 10px 22px;
}

.btn-save:hover {
  background: var(--primary-dark);
  transform: translateY(-1px);
}

.btn-save:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}

.btn-cancel {
  background: var(--surface);
  border: 1.5px solid var(--border);
  color: var(--body);
  padding: 10px 20px;
}

.btn-cancel:hover { border-color: var(--muted); }

/* Header-sized variant: the "+ New Portal Client" button sits in the header */
.portal-header .btn-primary {
  padding: 8px 16px;
  font-size: 13px;
}

.btn-ghost {
  padding: 8px 14px;
  background: var(--subtle);
  border: 1.5px solid var(--border);
  border-radius: 8px;
  color: var(--heading);
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s;
}

.btn-ghost:hover {
  border-color: var(--primary);
  color: var(--primary);
}

.btn-copy {
  padding: 6px 12px;
  background: var(--primary);
  color: var(--white);
  border: none;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.2s;
}

.btn-copy:hover { background: var(--primary-dark); }

/* ==================== STATUS PILLS (client summaries) ==================== */
.status-pill {
  font-size: 12px;
  font-weight: 600;
  padding: 2px 10px;
  border-radius: 99px;
}

.status-pill.down { background: #fecaca; color: #7f1d1d; }

.status-pill.live { background: #dcfce7; color: #166534; }

.status-pill.pending { background: #fef9c3; color: #854d0e; }

.status-pill.issue { background: #fee2e2; color: #991b1b; }

.status-pill.declined { background: #f1f5f9; color: #64748b; }

.status-pill.accepted { background: #dcfce7; color: #166534; }

/* ==================== CLIENT LINKS / DOCUMENTS ==================== */
.client-section { margin-bottom: 14px; }

.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
}

.chip-link {
  display: inline-block;
  padding: 5px 12px;
  background: var(--subtle);
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--heading);
  text-decoration: none;
  transition: border-color 0.2s, color 0.2s, background 0.2s;
}

.chip-link:hover {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-light);
}

/* ==================== MODALS ==================== */
.modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(15, 23, 42, 0.5);
}

.modal.active {
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-content {
  position: relative;
  background: var(--surface);
  padding: 30px;
  border-radius: 12px;
  max-width: 500px;
  width: 90%;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}

.modal-content--wide { max-width: 680px; }

.page-admin .modal-header {
  font-size: 20px;
  font-weight: 700;
  color: var(--heading);
  margin-bottom: 20px;
}

.modal-close {
  position: absolute;
  top: 14px;
  right: 20px;
  font-size: 28px;
  color: var(--muted);
  cursor: pointer;
  line-height: 1;
  transition: color 0.2s;
}

.modal-close:hover { color: var(--heading); }

/* ---------- Privacy notice, opened in place on the coming-soon page ----------
   Reuses .modal / .modal-content / .modal-close above, so the overlay, surface,
   radius and shadow are the ones the rest of the site already uses. Nothing
   here introduces a color that is not already a token. */
/* The card's background AND its ink come from the theme. The backdrop stays the
   plain dim scrim .modal already sets - the page is not tinted.

   --footer-* is the only family in this palette that carries a genuinely dark
   surface and flips on its own. Every pair below is already contrast-checked at
   the top of this file and in the footer that ships today:

     dark    #94a3b8 on #0f172a -> 6.96      light   #475569 on #f1f5f9 -> 7.40
             #ffffff on #0f172a -> 17.9              #0f172a on #f1f5f9 -> 16.4  */
/* No scrim. .modal above dims the page behind with rgba(15, 23, 42, 0.5); the
   notice sits over the page as-is instead. The overlay element still covers the
   viewport so a click outside the card closes it. */
#privacyModal { background: transparent; }

/* --forest-tint is production's section green, #376f57, and it flips to the pale
   #eef7f1 in light mode - so the scheme decides both the surface and the ink.
   Rule 3 at the top of this file governs what may sit on it: dark ink is only
   3.04:1 there, so the ink below is the cream --tan-text (5.21:1) and white
   (5.88:1), the two values already cleared for this surface. In light mode both
   flip to dark green on pale, measured 9.7:1. */
.privacy-modal {
  max-width: 34rem;
  padding: 2.25rem 2rem;
  background: var(--forest-tint);
}

.privacy-modal h2 { font-size: 1.6rem; color: var(--on-forest-strong); margin: 0 0 0.3rem; }

.privacy-modal .updated { font-size: 0.85rem; color: var(--tan-text); margin: 0 0 1.25rem; }

.privacy-modal p { color: var(--tan-text); line-height: 1.75; }

.privacy-modal a { color: var(--on-forest); text-decoration: underline; }

/* .modal-close asks for --muted, which this stylesheet references in nine places
   but never declares - it did not survive the 2026-08-09 consolidation. The
   property is therefore invalid at computed-value time and the button inherits
   whatever ink is around it. --body is the declared token for exactly this. */
.privacy-modal .modal-close {
  color: var(--tan-text);
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
}

.privacy-modal .modal-close:hover { color: var(--on-forest-strong); }

.confirm-modal { text-align: center; }

.confirm-modal p {
  color: var(--body);
  font-size: 14px;
  line-height: 1.55;
  margin-bottom: 6px;
}

.modal-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  margin-top: 20px;
}

.modal-actions--center { justify-content: center; }

.modal-actions--sticky {
  position: sticky;
  bottom: 0;
  background: var(--surface);
  padding: 12px 0 2px;
  border-top: 1px solid var(--border);
  margin-top: 18px;
}

/* ==================== FORMS ==================== */
.page-admin .form-group { margin-bottom: 15px; }

.page-admin .form-group label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--heading);
  margin-bottom: 6px;
}

.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  color: var(--heading);
  background: var(--surface);
  transition: border-color 0.2s, box-shadow 0.2s;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

.form-group textarea {
  resize: vertical;
  min-height: 100px;
}

.form-group label.checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 0;
  margin-top: 28px; /* align with adjacent text-input rows */
}

.form-group label.checkbox-label input[type="checkbox"] {
  width: auto;
}

.editor-rows { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }

.editor-row {
  display: grid;
  grid-template-columns: 1fr 1.5fr auto;
  gap: 8px;
  align-items: center;
}

.editor-row input {
  padding: 8px 10px;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  font-size: 13px;
  font-family: inherit;
  color: var(--heading);
}

.editor-row .btn-remove-row {
  background: none;
  border: none;
  color: #991b1b;
  cursor: pointer;
  font-size: 16px;
  padding: 4px 8px;
}

/* Client modal layout */
.client-modal-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.client-modal-grid .full-col { grid-column: 1 / -1; }

.modal-section-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 14px 0 6px;
  grid-column: 1 / -1;
}

.hint {
  font-size: 11px;
  color: var(--muted);
  margin-top: 4px;
}

/* Access-code widgets */
.code-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.code-input {
  font-family: 'Courier New', Courier, monospace;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 4px;
  width: 140px;
  text-align: center;
  padding: 6px 10px;
}

.code-chip {
  font-family: 'Courier New', Courier, monospace;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 4px;
  color: var(--heading);
  background: #f0fdf4;
  padding: 4px 16px;
  border-radius: 8px;
  border: 1px solid #bbf7d0;
}

/* Post-create code reveal */
.code-reveal {
  text-align: center;
  padding: 24px 0;
}

.code-reveal-icon {
  font-size: 40px;
  margin-bottom: 8px;
}

.code-reveal-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--heading);
  margin-bottom: 22px;
}

.code-reveal-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin-bottom: 10px;
}

.code-reveal-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 14px;
}

.code-reveal-code {
  font-family: 'Courier New', Courier, monospace;
  font-size: 36px;
  font-weight: 700;
  letter-spacing: 8px;
  color: var(--heading);
  background: var(--subtle);
  padding: 14px 28px;
  border-radius: 8px;
}

.code-reveal-done { margin-top: 24px; }

/* ==================== RESPONSIVE ==================== */
@media (max-width: 768px) {
  .header-inner { padding: 14px 16px; }
  .header-subtitle,
  .client-name { display: none; }
  .tabs-inner { padding: 0 8px; overflow-x: auto; }
  .tab-btn { padding: 13px 13px; font-size: 13px; }
  .portal-main { padding: 20px 16px 40px; }
  .client-modal-grid { grid-template-columns: 1fr; }
  .message-actions { flex-wrap: wrap; }
  /* Stack the search below the heading on small screens */
  .panel-heading-row {
    grid-template-columns: 1fr;
    gap: 10px;
  }
  .panel-heading-balance { display: none; }
}


/* ══════════════════════════════════════════════════════════════════
   THEME TOGGLE — injected by js/theme.js into the home page navbar.
   Every other page reads the stored choice and follows it, so there is
   exactly one place on the site where the theme is changed.
   ══════════════════════════════════════════════════════════════════ */


.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  padding: 0;
  border: 1.5px solid var(--primary);
  border-radius: 6px;
  background: transparent;
  color: var(--primary);
  cursor: pointer;
  margin-right: 6px;
  transition: background-color 0.2s, color 0.2s;
}

/* Matches .nav-login's hover: the control is green at rest, so a green tint
   rather than a green fill is what actually reads as a state change. */
.theme-toggle:hover { background-color: rgba(22, 163, 74, 0.18); }  /* --primary @ 18% */

.theme-toggle:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* Windows high-contrast replaces authored colours with system ones. The button
   is an icon in a tinted box, so without an explicit border it loses its edge
   entirely and reads as a stray glyph in the nav. */
@media (forced-colors: active) {
  .theme-toggle {
    border-color: ButtonBorder;
    color: ButtonText;
  }

  .theme-toggle:focus-visible { outline-color: Highlight; }
}



/* ══════════════════════════════════════════════════════════════════
   PRELAUNCH ADDITIONS — LogansDevWebsitePrelaunch only
   ------------------------------------------------------------------
   Everything above is site.css from LogansDevWebsite, with one change:
   a stray "}" at its line 2980 was removed. It is unbalanced in the
   original (510 open, 511 close), which makes a browser discard the
   rule that follows it. Worth fixing upstream.

   No token is redeclared. Three inherited rules are deliberately
   overridden, each noted where it appears: .hero padding-bottom,
   light-theme hero ink, .form-status margins.
   ══════════════════════════════════════════════════════════════════ */

/* The inherited .hero pairs min-height:100vh with padding-bottom:54vh so
   the illustration runs into the section below. This page has no section
   below it, so that overshoot pushed the street scene off the fold.
   These are the largest values that keep the hero inside one viewport. */
.hero { padding-bottom: 48vh; }

/* ---------- signup form ---------- */

.signup { max-width: 27rem; margin: 0 auto; }
.signup-row { display: flex; gap: 0.75rem; }

/* Bot trap. Off-screen rather than display:none so headless scrapers still
   fill it. Hidden from assistive tech in the markup with aria-hidden. */
.honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.signup input[type="email"] {
  flex: 1;
  min-width: 0;
  font-family: var(--font);
  font-size: 0.95rem;
  padding: 0.8rem 1rem;
  border-radius: 8px;
  border: 2px solid transparent;
  background: rgba(255, 255, 255, 0.95);
  color: var(--heading);
}
.signup input[type="email"]::placeholder { color: #64748b; }
.signup input[type="email"]:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.35);
}
.signup .btn-accent[disabled] { opacity: 0.65; cursor: default; transform: none; }
.signup .btn-accent:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }

/* Overrides the inherited 32px paragraph margin from ".hero p", which
   outranks a bare class - ".hero p.x" is what applies. */
.hero p.form-status {
  margin: 0.5rem 0 0;
  min-height: 1.1rem;
  font-size: 0.92rem;
  color: var(--hero-body);
}

/* The two states must be distinguishable from each other and from plain
   status text, and they sit over the illustration. Specificity has to beat
   ":root[data-theme=light] .hero p" (0,3,1). */
.hero p.form-status.is-error { color: #fecaca; font-weight: 600; }
.hero p.form-status.is-success { color: var(--primary-tint); font-weight: 600; }
:root[data-theme="light"] .hero p.form-status.is-error { color: #991b1b; font-weight: 600; }
:root[data-theme="light"] .hero p.form-status.is-success { color: var(--primary-dark); font-weight: 600; }

/* ---------- light-theme hero ink ----------
   Light theme puts the copy over the DAYTIME illustration, a sky averaging
   rgb(94,167,201). Measured there: --body 2.83:1 fails, --primary-dark
   1.88:1 worse, --heading 6.34:1 passes. Dark theme is untouched. */
:root[data-theme="light"] .hero p { color: var(--heading); }
:root[data-theme="light"] .hero-tagline { color: var(--heading); }

/* ---------- theme toggle placement ----------
   The toggle's inner edge sits the same distance from the bar's edge as the
   wordmark's inner edge: 24px padding + 157px wordmark = 181 on the left,
   24 + 121 + 36 = 181 on the right. Re-measure the 121px if the wordmark
   changes. The live navbar needs none of this - its links fill the space. */
.nav-inner .theme-toggle { margin-left: auto; margin-right: 121px; }

/* ---------- narrow screens ---------- */
@media (max-width: 640px) {
  .signup-row { flex-direction: column; }

  /* A 16:9 illustration cropped to 390px wide shows only its middle quarter,
     just the church spire, so the focal point shifts left to keep the
     storefronts in frame. */
  .hero { padding-bottom: 32vh; background-position: bottom 0 left 38%; }
  .hero::before { background-position: bottom 0 left 38%; }

  /* The mirror needs 2x181 + 36 = 398px of bar; below that the boxes would
     collide, so the toggle returns to the right edge. */
  .nav-inner .theme-toggle { margin-right: 0; }
}
