/* ==========================================================================
   OpsPulse — site additions
   --------------------------------------------------------------------------
   Components the shared design system does not have yet: the nav's Ops Floor
   tab and the contact form. Kept out of styles.css so that file stays
   byte-identical to the copy on the other branch and the two can still be
   diffed cleanly.

   Everything below is built from the tokens already declared in
   styles.css :root. No new colours, radii or easings are introduced.
   ========================================================================== */

/* ---------- Nav: Ops Floor tab ----------
   This lives in .nav-cta rather than .nav-links deliberately. .nav-links is
   display:none below 1180px, so a link placed there would vanish on exactly
   the screens where hunting for the demo is most annoying. .nav-cta stays
   visible at every width. */
.nav-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px;
  border-radius: 100px;
  border: 1px solid rgba(52, 211, 153, 0.28);
  background: rgba(52, 211, 153, 0.07);
  color: var(--text-soft);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  transition: color 0.2s, background 0.2s, border-color 0.2s, transform 0.18s var(--ease);
}
.nav-tab:hover {
  color: var(--text);
  background: rgba(52, 211, 153, 0.13);
  border-color: rgba(52, 211, 153, 0.45);
  transform: translateY(-1px);
}
.nav-tab .dot-live { width: 6px; height: 6px; }

/* ---------- Nav: layout ----------
   styles.css lays the bar out as three flex groups under
   `justify-content: space-between` with no `gap`. space-between only
   distributes *slack*, and there was none: at full desktop width the three
   groups measured roughly 1195px inside an 1132px content column. So the
   groups butted straight into each other — the wordmark ran into "Product",
   "Contact" ran into the Ops Floor tab — and the two labels containing a
   space, "External Signals" and "Design Partners", broke onto a second line
   inside a 68px bar.

   Spacing alone could not fix a row that does not fit. Three things gave, in
   the markup rather than here:

     - "Contact" left the link row. It pointed at #contact, which is where the
       "Schedule a demo →" button 40px to its right already goes.
     - "Log in" stopped being a third pill. Three buttons of near-equal weight
       in one corner read as a toolbar rather than a hierarchy; it is now a
       quiet text link next to the one primary CTA and the live Ops Floor tab.
     - The section links collapse into a menu below 1180px — the width at
       which .wrap stops being at its maximum — rather than staying crammed
       in down to 860px and then disappearing with nowhere to go.

   The rules below then make a collision structurally impossible rather than
   merely unlikely: a floor under the gap between the groups, no wrapping
   inside a label, and no shrinking of the two groups that anchor the ends. */
.nav-inner { gap: clamp(18px, 3vw, 44px); }
.nav-inner > .brand,
.nav-inner > .nav-cta { flex: 0 0 auto; }
.nav-links {
  flex: 1 1 auto;
  justify-content: center;
  gap: clamp(20px, 2.3vw, 32px);
  min-width: 0;
}
.nav-links a { position: relative; padding: 6px 0; white-space: nowrap; }

/* The hover underline is drawn on a ::after rather than with text-decoration
   so it can animate and so it sits clear of the descenders in "Design". */
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1.5px;
  border-radius: 2px;
  background: var(--emerald);
  transform: scaleX(0);
  transition: transform 0.22s var(--ease);
}
.nav-links a:hover::after,
.nav-links a:focus-visible::after { transform: scaleX(1); }

/* The wordmark carries the page, so it gets the tighter tracking a logotype
   wants and its own no-wrap. " AI" was an inline style with a leading space
   inside the span, which put the gap at the mercy of whitespace collapsing;
   it is a margin now, so the suffix cannot end up touching the word. */
.brand { font-size: 1.15rem; letter-spacing: -0.015em; white-space: nowrap; }
.brand-ai {
  margin-left: 0.32em;
  font-weight: 600;
  letter-spacing: 0.01em;
  opacity: 0.55;
}

.nav-login {
  color: var(--text-soft);
  font-size: 0.9rem;
  font-weight: 600;
  white-space: nowrap;
  transition: color 0.2s;
}
.nav-login:hover { color: var(--text); }

/* ---------- Nav: compact menu ----------
   The button is hidden at desktop width and the panel is built by nav.js from
   the .nav-links markup, so there is exactly one list of section links in the
   page. Without JS the button never appears and the panel is never populated,
   which leaves the pre-existing behaviour below the breakpoint rather than an
   empty control. */
.nav-toggle {
  display: none;
  width: 40px; height: 40px;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 11px;
  border: 1px solid var(--line-strong);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text);
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}
.nav-toggle:hover { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.22); }

/* Three bars from one element plus its two pseudo-elements, so the close X is
   a transform of the same shape rather than a second icon swapped in. */
.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  display: block;
  width: 17px; height: 1.8px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.22s var(--ease), background 0.16s;
}
.nav-toggle-bars { position: relative; }
.nav-toggle-bars::before,
.nav-toggle-bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle-bars::before { top: -5.5px; }
.nav-toggle-bars::after { top: 5.5px; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { transform: translateY(5.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after { transform: translateY(-5.5px) rotate(-45deg); }

/* Absolute rather than in the bar's flow: .nav is sticky, so it is already a
   containing block, and a panel that took up flow space would push the hero
   down by its own height every time it opened. */
.nav-menu {
  position: absolute;
  top: 100%;
  left: 0; right: 0;
  max-height: calc(100vh - 68px);
  overflow-y: auto;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  /* Opaque, not the translucent-plus-blur the bar uses. .nav already sets a
     backdrop-filter, which makes it a backdrop root: a nested backdrop-filter
     samples only inside that root, so the panel's blur silently did nothing
     and the hero mock read straight through it at 6% alpha. A menu you can
     read the page through is not a menu. */
  background: var(--bg);
  box-shadow: 0 22px 44px -26px rgba(0, 0, 0, 0.95);
  animation: navMenuIn 0.18s var(--ease);
}
.nav-menu[hidden] { display: none; }
.nav-menu-inner { display: flex; flex-direction: column; padding-top: 6px; padding-bottom: 14px; }

/* Scoped to the plain rows by :not([class]) rather than to every anchor in the
   panel. nav.js strips the class off a link it demotes to a row and leaves it
   on the tab and the button, so this selector separates the two cases exactly.
   Matching every `a` instead was a real bug and not only a tidiness one: at
   (0,1,1) it outranks .btn-primary at (0,1,0), so the demo CTA rendered in
   --text-soft on its own bright gradient. */
.nav-menu a:not([class]) {
  padding: 14px 2px;
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--text-soft);
  border-bottom: 1px solid var(--line);
  transition: color 0.2s;
}
.nav-menu a:not([class]):last-child { border-bottom: 0; }
.nav-menu a:not([class]):hover { color: var(--text); }

/* What the bar could not fit, in the order a visitor wants it: the section
   links, then the quiet ones, then the CTA as a full-width button — the one
   place in the panel with enough room to look like the primary action it is. */
.nav-menu .nav-tab {
  align-self: flex-start;
  margin-top: 14px;
  padding: 8px 14px;
}
.nav-menu .nav-tab .nav-tab-label { display: inline; }
.nav-menu .btn {
  justify-content: center;
  margin-top: 14px;
}
/* The lift on hover is for a button sitting in a row of its own, not for one
   filling the width of a panel. */
.nav-menu .btn:hover { transform: none; }

@keyframes navMenuIn {
  from { opacity: 0; transform: translateY(-6px); }
}
@media (prefers-reduced-motion: reduce) {
  .nav-menu { animation: none; }
  .nav-links a::after { transition: none; }
  .nav-toggle-bars,
  .nav-toggle-bars::before,
  .nav-toggle-bars::after { transition: none; }
}

/* 1180px is --maxw: above it .wrap is at its maximum and the full row fits
   with room to spare; below it the column starts shrinking and the links are
   the part that can move somewhere else. */
@media (max-width: 1180px) {
  .nav-links { display: none; }
  .nav-toggle { display: inline-flex; }
}

/* Below 720px the corner carries the whole nav. Drop "Log in" first — it is
   the least used of the three and both the footer and the menu still carry it
   — then tighten what is left. */
@media (max-width: 720px) {
  .nav-cta .nav-login { display: none; }
}
/* On a phone the bar is the wordmark and the button, and nothing else. The
   corner previously tried to hold the Ops Floor tab, a login button and the
   demo CTA at 390px inside a 342px column, which overflowed by 54px and gave
   the whole page a horizontal scrollbar. Those three are not dropped: nav.js
   moves whichever of them the stylesheet has hidden into the panel, where the
   demo CTA gets a full-width button instead of a squeezed pill. */
@media (max-width: 620px) {
  .nav-cta { gap: 9px; }
  .nav-cta .nav-tab,
  .nav-cta .btn { display: none; }
  .nav-toggle { width: 38px; height: 38px; }
}

/* ---------- Founder titles ----------
   The base rule renders the role at 0.72rem in --text-dim, which disappears
   next to the name. Who holds which title is one of the first things a buyer
   checks on a company this small, so it gets brand colour, its own line and
   enough weight to be read at a glance rather than hunted for. */
.founder h4 {
  display: block;
  font-size: 1.12rem;
}
.founder h4 .role {
  display: block;
  margin-top: 5px;
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--emerald);
  letter-spacing: 0.1em;
}

/* ---------- Contact form ---------- */
.cform { margin-top: 26px; text-align: left; }

.cform-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.cform-grid .cf-wide { grid-column: 1 / -1; }
@media (max-width: 640px) {
  .cform-grid { grid-template-columns: 1fr; }
}

.cf-field { display: flex; flex-direction: column; gap: 6px; }

.cf-field label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.cf-field label .req { color: var(--emerald); }

.cf-field input,
.cf-field select,
.cf-field textarea {
  width: 100%;
  padding: 11px 13px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: rgba(0, 0, 0, 0.25);
  color: var(--text);
  font: inherit;
  font-size: 0.9rem;
  transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
}
.cf-field textarea { resize: vertical; min-height: 96px; line-height: 1.6; }
.cf-field input::placeholder,
.cf-field textarea::placeholder { color: var(--text-dim); }

.cf-field input:focus,
.cf-field select:focus,
.cf-field textarea:focus {
  outline: none;
  border-color: rgba(52, 211, 153, 0.55);
  box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.12);
  background: rgba(0, 0, 0, 0.35);
}

/* The browser's own invalid styling fires before the field has ever been
   touched, which flags a form the visitor has not started filling in. Scope it
   to fields that actually have content instead. */
.cf-field input:not(:placeholder-shown):invalid {
  border-color: rgba(251, 113, 133, 0.55);
}

/* select needs its own arrow once appearance is stripped, or it renders as a
   flat box with no affordance on Windows. */
.cf-field select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='m6 9 6 6 6-6' stroke='%23737f9e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 16px;
}
.cf-field select option { background: var(--panel); color: var(--text); }

.cf-check {
  grid-column: 1 / -1;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 0.85rem;
  color: var(--text-soft);
  line-height: 1.5;
  cursor: pointer;
}
.cf-check input {
  width: 17px; height: 17px;
  margin-top: 2px;
  flex: 0 0 auto;
  accent-color: #34d399;
  cursor: pointer;
}

.cform-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-top: 20px;
}
.cform-actions .btn { cursor: pointer; border: 0; font-family: inherit; }

.cf-note {
  font-size: 0.78rem;
  color: var(--text-dim);
  line-height: 1.55;
  margin: 0;
  max-width: 46ch;
}

/* ---------- Sent confirmation ----------
   Shown after submit. The form composes a message and hands it to the
   visitor's mail client; if no client is configured nothing appears to
   happen, so the composed text is shown here with a copy button as the
   fallback path rather than leaving them guessing. */
.cf-sent {
  margin-top: 22px;
  padding: 20px 22px;
  border: 1px solid rgba(52, 211, 153, 0.32);
  border-radius: var(--radius);
  background: rgba(52, 211, 153, 0.06);
}
.cf-sent[hidden] { display: none; }
.cf-sent h4 {
  display: flex; align-items: center; gap: 9px;
  font-size: 1rem; color: var(--text); margin: 0 0 8px;
}
.cf-sent h4 svg { width: 19px; height: 19px; flex: 0 0 auto; }
.cf-sent p { font-size: 0.87rem; color: var(--text-soft); line-height: 1.65; margin: 0 0 12px; }

.cf-sent pre {
  max-height: 190px;
  overflow: auto;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(0, 0, 0, 0.35);
  color: var(--text-soft);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0 0 14px;
}
.cf-sent-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.cf-copied { font-size: 0.8rem; color: var(--emerald); font-weight: 600; }
.cf-copied[hidden] { display: none; }

/* ---------- Hero: the lead renewal card ----------
   The second alert in the hero mock stays a plain .alert-row. This one is
   promoted to a full card because it is the product's whole argument in one
   object: a named account, a date, exposure as a range, the two signals that
   moved, and the play to run. A director should be able to read it top to
   bottom and know what their next hour is for.

   Built entirely from styles.css tokens. No new colours are introduced. */
.risk-card {
  background: var(--panel-3);
  border: 1px solid var(--line);
  border-left: 3px solid var(--danger);
  border-radius: 10px;
  padding: 12px 13px 11px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.risk-top { display: flex; align-items: center; gap: 11px; }
.risk-top > svg { flex: 0 0 auto; }
.risk-name { font-size: 0.82rem; font-weight: 650; color: var(--text); min-width: 0; }
.risk-name small { display: block; font-size: 0.72rem; font-weight: 500; color: var(--text-dim); margin-top: 1px; }

/* Exposure sits on its own line at display size: it is the number that decides
   whether this account outranks the other four, so it should not have to
   compete with the account name for the eye. */
.risk-exp { display: flex; align-items: baseline; gap: 8px; }
.risk-exp b {
  font-size: 1.16rem; font-weight: 800; letter-spacing: -0.02em;
  background: var(--grad); -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.risk-exp span { font-size: 0.7rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.07em; font-weight: 600; }

.risk-signals { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.risk-signals li { font-size: 0.745rem; color: var(--text-dim); line-height: 1.45; }
.risk-signals i { font-style: normal; font-weight: 700; }
.risk-signals i.dn { color: var(--danger); }
.risk-signals i.fl { color: var(--warn); }

/* The line that separates a decision engine from a dashboard. Tinted and
   ruled off so it reads as the card's conclusion rather than a fourth fact. */
.risk-act {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  padding-top: 9px; border-top: 1px dashed var(--line-strong);
}
.risk-act .ra-k {
  font-size: 0.63rem; text-transform: uppercase; letter-spacing: 0.1em;
  font-weight: 700; color: var(--text-dim); flex: 0 0 auto;
}
.risk-act strong { font-size: 0.8rem; font-weight: 700; color: var(--emerald); }

/* ---------- Trust bar: the proof line ----------
   Sits under the role chips at reading size rather than caption size, because
   it is a claim someone is meant to weigh, not a footnote. The trailing
   sentence is dimmed one step: it is the honest caveat about missing logos,
   and it should read as candour rather than as the headline.

   Selector is .trust .trust-proof rather than .trust-proof alone because
   styles.css already sets .trust p to uppercase caption styling, and that is
   the more specific selector of the two. */
.trust .trust-proof {
  max-width: 82ch;
  margin: 20px auto 0;
  text-align: center;
  font-size: 0.83rem;
  line-height: 1.65;
  color: var(--text-soft);
  text-transform: none;
  letter-spacing: normal;
}
.trust .trust-proof span { color: var(--text-dim); }

/* ---------- The 60-second experiment ----------
   Numbered steps for the upload path. A real <ol> rather than styled divs, so
   the order is conveyed to a screen reader too and the numerals survive with
   CSS off. The counter is drawn rather than using the default marker because
   the default sits outside the content box and cannot take the gradient. */
.try-steps {
  list-style: none;
  counter-reset: tryStep;
  margin: 22px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.try-steps li {
  counter-increment: tryStep;
  position: relative;
  padding-left: 40px;
  font-size: 0.87rem;
  line-height: 1.65;
  color: var(--text-soft);
}
.try-steps li::before {
  content: counter(tryStep);
  position: absolute;
  left: 0;
  top: -1px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: var(--grad);
  color: #04120c;
  font-size: 0.78rem;
  font-weight: 800;
}
.try-steps li b { color: var(--text); font-weight: 650; }

/* The scope caveat above the steps. Amber rather than emerald and ruled on the
   leading edge, because it is a qualification of the promise directly above it,
   not another benefit — the two must not be skimmable as the same thing. It
   sits BEFORE .try-steps deliberately: a caveat under the call to action is a
   disclaimer, and a disclaimer is what you write when you would rather it were
   not read. */
.src-caveat {
  margin: 20px 0 0;
  padding: 13px 15px 12px 16px;
  border: 1px solid rgba(251, 191, 36, 0.22);
  border-left: 2px solid var(--amber, #fbbf24);
  border-radius: var(--radius);
  background: rgba(251, 191, 36, 0.045);
  font-size: 0.815rem;
  line-height: 1.68;
  color: var(--text-soft);
}
.src-caveat b { color: var(--text); font-weight: 650; }

/* The outcome block. Ruled off and tinted so it reads as the payoff of the
   three steps above it rather than a fourth step. */
.try-out {
  margin-top: 24px;
  padding: 16px 18px 15px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: linear-gradient(180deg, rgba(52, 211, 153, 0.055), transparent);
}
.try-out-k {
  display: block;
  font-size: 0.63rem;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  font-weight: 800;
  color: var(--emerald);
  margin-bottom: 11px;
}
.try-out ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.try-out li {
  position: relative;
  padding-left: 17px;
  font-size: 0.83rem;
  line-height: 1.6;
  color: var(--text-soft);
}
.try-out li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--emerald);
}
.try-out li b { color: var(--text); font-weight: 650; }
.try-out-note {
  margin: 13px 0 0;
  padding-top: 11px;
  border-top: 1px dashed var(--line-strong);
  font-size: 0.775rem;
  line-height: 1.6;
  color: var(--text-dim);
}

/* ---------- External-signal status tag ----------
   A card-level "not built yet" marker. Cyan rather than emerald because every
   emerald affordance on this site means "this runs" — reusing it for a planned
   detector would say the opposite of what the label says. It is a plain inline
   element inside .card, sitting after the paragraph, so it inherits the card's
   own hover and reveal behaviour and needs no JS. */
.status-tag {
  display: inline-block;
  margin-top: 13px;
  padding: 3px 9px 4px;
  border: 1px dashed rgba(34, 211, 238, 0.34);
  border-radius: 999px;
  font-size: 0.63rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 800;
  color: var(--cyan);
  background: rgba(34, 211, 238, 0.05);
}
/* Funding is the one signal that runs toward the upside, so it takes the
   emerald that means "this is good news" everywhere else on the site rather
   than reading as a fourth flavour of risk. */
.status-tag.ok {
  border-color: rgba(52, 211, 153, 0.34);
  color: var(--emerald);
  background: rgba(52, 211, 153, 0.05);
}

/* ---------- Footer link row ----------
   The base rule is a flex row with no wrap, so on a phone the five links ran
   straight off the side and gave the page a 692px scroll width inside a 390px
   viewport — the horizontal scrollbar was the footer's, not the nav's. */
.foot-links { flex-wrap: wrap; row-gap: 10px; }
