/* global React, ReactDOM, LeftSidebar, SiteHeader, SiteFooter, Lenis, ConsoleVoices */
/*
 * Virtue Console — product page.
 *
 * ANNOUNCES. DOES NOT SELL. Rodney's decision, 2026-09-03: no price, no buy
 * button, no download. There is nothing to license yet, nothing signed, and no
 * build for a second platform, so a purchase path here would be a promise the
 * product cannot keep.
 *
 * EVERY CLAIM ON THIS PAGE IS PINNED IN FACTS.json and `npm run check:facts`
 * is watching six Console rules. Before editing a number here, read the rule:
 *
 *   console-inserts-nothing   "Opening Console inserts nothing" is TRUE and is
 *                             the best thing about the product. The old claim
 *                             that we contribute no processing of our own went
 *                             FALSE on 2026-09-03 and must never return. The
 *                             two are not the same claim. The rule lists the
 *                             exact wordings that are blocked - read it there.
 *   console-oversampling      13.6 dB, from 1479 to 65. Not rounded, not a
 *                             percentage, and never described as abolishing
 *                             the problem. Latency is DECLARED, not absent.
 *   console-vwide             Widens above 150 Hz only. The origin of that code
 *                             is never named in site copy - standing policy.
 *   console-vca-not-new       REAPER has had VCA groups since v5. Better
 *                             presentation, never a new capability.
 *   console-metering-honesty  Implements the BS.1770-4 formula. Has NOT been
 *                             run against the Tech 3341 conformance files.
 *                             "Bit-transparent" is true of the METERING PATH
 *                             only, never of the product.
 *   console-not-shipping      No price, no purchase, macOS and REAPER only.
 *
 * NOTE FOR THE NEXT PERSON: check:facts scans this whole file, comments and
 * all. It cannot tell a warning from a claim, so a comment that QUOTES a
 * forbidden phrase fails the build exactly as the phrase itself would. That is
 * the right trade - it caught this header on its first run - but it means
 * warnings here have to describe the banned wording rather than repeat it.
 *
 * The feature list is the "Built" section of VIRTUE_CONSOLE/docs/REQUIREMENTS.md
 * and nothing else. The "Asked for, not built" table in that file - monitor
 * section, cue sends, MIDI learn, skins, VCA - must never appear here as a
 * page section. Neve owns that file; ask her before adding a row.
 */

const { useEffect: useEffectC } = React;

const useLenisC = () => {
  useEffectC(() => {
    if (typeof Lenis === 'undefined') return;
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    const lenis = new Lenis({ duration: 0.6, smoothWheel: true });
    let raf;
    const tick = (t) => { lenis.raf(t); raf = requestAnimationFrame(tick); };
    raf = requestAnimationFrame(tick);
    return () => { cancelAnimationFrame(raf); lenis.destroy(); };
  }, []);
};

/* A VU face drawn to Console's own geometry, not a picture of one.
 * Numbers from VIRTUE_CONSOLE/assets/templates/README.md, which is generated by
 * tools/make_vu_template.py - the same source drawVuMeter() reads, so the art
 * and the renderer cannot drift:
 *   face 240 x 178, pivot (120, 231.4) - 53.4px BELOW the face, which is what
 *   gives a VU its shallow arc - arc radius 186.9, sweep +/-27 deg from
 *   vertical, 0 VU at 70.8% of the sweep because a VU deflects with rectified
 *   average voltage rather than with decibels. That is why the ticks crowd
 *   toward the top of the scale instead of spacing evenly.
 * Drawn, not photographed: there is no Console screenshot yet and a mocked-up
 * one would be a picture of something that does not exist.
 */
const VuFace = () => (
  <svg viewBox="0 0 240 178" xmlns="http://www.w3.org/2000/svg" role="img"
       style={{ width: '100%', maxWidth: 300, height: 'auto' }}
       aria-label="A VU meter face drawn to Virtue Console's own geometry: 54 degrees of sweep, with 0 VU at 70.8 percent of it.">
    <path d="M 49.6 66.9 A 178.9 178.9 0 0 1 201.2 72.0" fill="none" stroke="var(--line-strong)" strokeWidth="1"/>
    <path d="M 154.8 55.9 A 178.9 178.9 0 0 1 201.2 72.0" fill="none" stroke="var(--danger)" strokeWidth="2.5"/>
    <line x1="49.6" y1="66.9" x2="52.7" y2="74.3" stroke="var(--t-2)" strokeWidth="1.6"/>
    <text x="58.2" y="87.2" textAnchor="middle" dominantBaseline="middle" fontFamily="var(--mono)" fontSize="11" fill="var(--t-2)">-20</text>
    <line x1="74.0" y1="58.5" x2="76.0" y2="66.3" stroke="var(--t-2)" strokeWidth="1.6"/>
    <text x="79.6" y="79.8" textAnchor="middle" dominantBaseline="middle" fontFamily="var(--mono)" fontSize="11" fill="var(--t-2)">-10</text>
    <line x1="89.2" y1="55.2" x2="89.9" y2="59.1" stroke="var(--t-2)" strokeWidth="1"/>
    <line x1="102.9" y1="53.3" x2="103.6" y2="61.3" stroke="var(--t-2)" strokeWidth="1.6"/>
    <text x="105.0" y="75.2" textAnchor="middle" dominantBaseline="middle" fontFamily="var(--mono)" fontSize="11" fill="var(--t-2)">-5</text>
    <line x1="120.2" y1="52.5" x2="120.2" y2="60.5" stroke="var(--t-2)" strokeWidth="1.6"/>
    <text x="120.2" y="74.5" textAnchor="middle" dominantBaseline="middle" fontFamily="var(--mono)" fontSize="11" fill="var(--t-2)">-3</text>
    <line x1="130.5" y1="52.8" x2="130.3" y2="56.8" stroke="var(--t-2)" strokeWidth="1"/>
    <line x1="142.0" y1="53.9" x2="141.5" y2="57.8" stroke="var(--t-2)" strokeWidth="1"/>
    <line x1="154.8" y1="55.9" x2="153.3" y2="63.8" stroke="var(--t-2)" strokeWidth="1.6"/>
    <text x="150.6" y="77.5" textAnchor="middle" dominantBaseline="middle" fontFamily="var(--mono)" fontSize="11" fill="var(--t-2)">0</text>
    <line x1="169.0" y1="59.3" x2="167.9" y2="63.2" stroke="var(--t-2)" strokeWidth="1"/>
    <line x1="184.5" y1="64.5" x2="183.1" y2="68.3" stroke="var(--t-2)" strokeWidth="1"/>
    <line x1="201.2" y1="72.0" x2="197.6" y2="79.1" stroke="var(--t-2)" strokeWidth="1.6"/>
    <text x="191.2" y="91.6" textAnchor="middle" dominantBaseline="middle" fontFamily="var(--mono)" fontSize="11" fill="var(--t-2)">+3</text>
    {/* The needle takes the accent. It is the only live element on the face,
        which is the whole argument for what the accent is for. */}
    <line x1="120.0" y1="231.4" x2="120.2" y2="48.5" stroke="var(--o)" strokeWidth="2.2" strokeLinecap="round"/>
    <text x="120" y="162" textAnchor="middle" fontFamily="var(--mono)" fontSize="10" letterSpacing="2" fill="var(--t-3)">VU</text>
  </svg>
);

const Eyebrow = ({ children }) => (
  <p className="eyebrow" style={{ marginBottom: 12 }}>{children}</p>
);

const Rule = () => (
  <hr style={{ border: 0, borderTop: '1px solid var(--line)', margin: '0' }} />
);

/* One built feature. Rules, not cards with shadows. */
const Feature = ({ title, children }) => (
  <div style={{ padding: '28px 0', borderTop: '1px solid var(--line)' }}>
    <h3 style={{ fontSize: 17, marginBottom: 8, color: 'var(--t)' }}>{title}</h3>
    <p style={{ color: 'var(--t-2)', lineHeight: 1.65, margin: 0, maxWidth: '62ch' }}>{children}</p>
  </div>
);

const Spec = ({ label, value }) => (
  <div style={{ display: 'grid', gridTemplateColumns: 'minmax(160px, 34%) 1fr', gap: 16, padding: '14px 0', borderTop: '1px solid var(--line)' }}>
    <div style={{ color: 'var(--t-3)', fontSize: 13, fontFamily: 'var(--mono)' }}>{label}</div>
    <div style={{ color: 'var(--t-2)', lineHeight: 1.6 }}>{value}</div>
  </div>
);

const Section = ({ id, children, tone }) => (
  <section
    id={id}
    className="section-shell"
    style={{
      paddingTop: 88,
      paddingBottom: 88,
      background: tone === 'sunk' ? 'var(--bg-2)' : 'transparent',
    }}
  >
    <div style={{ maxWidth: 1080, margin: '0 auto' }}>{children}</div>
  </section>
);

const Console = () => {
  useLenisC();

  return (
    <>
      <SiteHeader base="../../" />
      <LeftSidebar base="../../" />

      <main id="main">

        {/* ---- Hero ------------------------------------------------------ */}
        <Section id="top">
          <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.35fr) minmax(0, 1fr)', gap: 56, alignItems: 'center' }} className="product-layout-grid">
            <div>
              <Eyebrow>Virtue Creative Systems · In development</Eyebrow>
              <h1 style={{ fontSize: 'clamp(38px, 5vw, 60px)', lineHeight: 1.02, letterSpacing: '-0.04em', marginBottom: 20 }}>
                Virtue Console
              </h1>
              <p style={{ fontSize: 19, color: 'var(--t-2)', lineHeight: 1.6, maxWidth: '54ch', marginBottom: 14 }}>
                A mixer for REAPER with calibrated metering on every track, and a channel
                strip that stays out of the way until you switch it on.
              </p>
              <p style={{ fontSize: 15, color: 'var(--t-3)', lineHeight: 1.6, maxWidth: '54ch', marginBottom: 32 }}>
                Opening it inserts nothing into your project and changes nothing you did not ask
                it to change. That is a design rule, not a default.
              </p>
              <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
                <a href="../../contact/" className="btn-primary">Ask to be told when it ships</a>
                <a href="#processing" className="btn-ghost">What it does to the audio</a>
              </div>
              <p style={{ fontSize: 13, color: 'var(--t-3)', marginTop: 18, fontFamily: 'var(--mono)' }}>
                Not released. No price yet. macOS, inside REAPER.
              </p>
            </div>

            <figure style={{ margin: 0 }}>
              <div style={{ background: 'var(--surface)', border: '1px solid var(--line)', borderRadius: 'var(--radius)', padding: '28px 24px 8px' }}>
                <VuFace />
              </div>
              <figcaption className="media-caption" style={{ marginTop: 12 }}>
                A drawing, not a screenshot. The scale is plotted from Virtue Console's own
                geometry — 54 degrees of sweep, 0 VU at 70.8 per cent of it — which is why the
                marks crowd toward the top: a VU deflects with average voltage, not with decibels.
              </figcaption>
            </figure>
          </div>
        </Section>

        {/* ---- The constraint -------------------------------------------- */}
        <Section id="opt-in" tone="sunk">
          <Eyebrow>The rule everything else is built around</Eyebrow>
          <h2 style={{ fontSize: 'clamp(26px, 3.4vw, 38px)', letterSpacing: '-0.03em', marginBottom: 20, maxWidth: '20ch' }}>
            Opening Console inserts nothing.
          </h2>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: 32, marginTop: 8 }}>
            <div>
              <h3 style={{ fontSize: 16, marginBottom: 8 }}>Placement happens when you open it</h3>
              <p style={{ color: 'var(--t-2)', lineHeight: 1.65 }}>
                Never on project load. Opening an old session to find something new in the chain
                is the thing this rule exists to prevent.
              </p>
            </div>
            <div>
              <h3 style={{ fontSize: 16, marginBottom: 8 }}>Removing it is an instruction</h3>
              <p style={{ color: 'var(--t-2)', lineHeight: 1.65 }}>
                Take the probe off a track and it is not put back. Putting one back by hand
                overrides the opt-out. Your decision holds in both directions.
              </p>
            </div>
            <div>
              <h3 style={{ fontSize: 16, marginBottom: 8 }}>The channel strip is off by default</h3>
              <p style={{ color: 'var(--t-2)', lineHeight: 1.65 }}>
                It is opt-in, per track. Nothing is processed because you opened a mixer. If you
                never switch it on, it never runs — and it costs you nothing.
              </p>
            </div>
          </div>
        </Section>

        {/* ---- Built ------------------------------------------------------ */}
        <Section id="features">
          <Eyebrow>What is built today</Eyebrow>
          <h2 style={{ fontSize: 'clamp(26px, 3.4vw, 38px)', letterSpacing: '-0.03em', marginBottom: 8, maxWidth: '22ch' }}>
            A mixer, with the readings on it.
          </h2>
          <p style={{ color: 'var(--t-2)', maxWidth: '62ch', marginBottom: 28, lineHeight: 1.65 }}>
            Everything on this list works now. Anything not on it is not built, and is not
            described anywhere on this page as though it were.
          </p>

          <Feature title="A meter bridge across the top">
            Every track's meter in one row, scrolling in lockstep with the channels beneath it,
            so the bridge and the strip never disagree about which track you are looking at.
          </Feature>
          <Feature title="Analog meters, six scales">
            VU, PPM, K-12, K-14, K-20 and LUFS. Three sizes, one needle or two. A meter skin per
            track, with a global default, so the drum bus can read K-14 while the master reads
            loudness.
          </Feature>
          <Feature title="Key detection, ranked and honest">
            The three most likely keys for each track, in order, each with a confidence figure
            attached. A guess that tells you how much of a guess it is.
          </Feature>
          <Feature title="An FX list you can throw things at">
            Empty drop slots, drag to reorder, and drag-and-drop straight from Virtue FX Manager
            or from the Finder. A plugin dragged onto a channel lands on that channel.
          </Feature>
          <Feature title="Sends, routing and phase in the strip">
            The sends list, the routing row, phase, FX chain bypass and monitor cycling, all on
            the channel rather than three dialogs away.
          </Feature>
          <Feature title="Built to be rearranged">
            Reorder channels by dragging the scribble strip. Extra rows appear when the window is
            tall enough to hold them. Mouse wheel on a fader or a pan, double-click to reset, and
            REAPER's own transport shortcuts keep working while the window has focus.
          </Feature>
        </Section>

        {/* ---- The processing --------------------------------------------- */}
        <Section id="processing" tone="sunk">
          <Eyebrow>The processing</Eyebrow>
          <h2 style={{ fontSize: 'clamp(26px, 3.4vw, 38px)', letterSpacing: '-0.03em', marginBottom: 20, maxWidth: '24ch' }}>
            Switch the strip on and it is ours, all the way down.
          </h2>
          <p style={{ color: 'var(--t-2)', maxWidth: '64ch', marginBottom: 8, lineHeight: 1.7, fontSize: 17 }}>
            Filters, dynamics and a four-band equaliser on the channel. Summing, tape and a
            channel voice across the desk. A stereo stage at the end. None of it runs until you
            ask a track for it, and all of it is built to a standard that is worth paying for —
            which is the argument below, with the measurements attached.
          </p>

          <div style={{ marginTop: 44 }}>
            <h3 style={{ fontSize: 20, marginBottom: 10 }}>Every nonlinear stage is oversampled</h3>
            <p style={{ color: 'var(--t-2)', lineHeight: 1.7, maxWidth: '64ch' }}>
              A summing encode, a tape, a valve — anything nonlinear makes harmonics, and the
              harmonics above half the sample rate do not vanish. They fold back down and land on
              frequencies with no musical relationship to what you played. Harmonic distortion
              sounds like weight, because it lands on notes related to the note. Folded distortion
              sounds like grit, and it moves the <em>wrong way</em>: play a rising line and those
              products descend. That is most of why digital saturation sounds small next to the
              hardware it is copying.
            </p>
            <p style={{ color: 'var(--t-2)', lineHeight: 1.7, maxWidth: '64ch', marginTop: 14 }}>
              So we measured it. A 6.5 kHz tone through a hard nonlinearity, summing the energy in
              every FFT bin that is <em>not</em> a harmonic of the input — everything produced
              that has no relationship to what went in.
            </p>

            <div style={{ display: 'flex', gap: 40, flexWrap: 'wrap', margin: '28px 0 8px', alignItems: 'flex-end' }}>
              <div>
                <div style={{ fontFamily: 'var(--mono)', fontSize: 13, color: 'var(--t-3)', marginBottom: 6 }}>At the session rate</div>
                <div style={{ fontSize: 44, fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 1 }}>1479</div>
              </div>
              <div>
                <div style={{ fontFamily: 'var(--mono)', fontSize: 13, color: 'var(--t-3)', marginBottom: 6 }}>At 4x</div>
                <div style={{ fontSize: 44, fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 1, color: 'var(--o-dim)' }}>65</div>
              </div>
              <div style={{ paddingBottom: 4 }}>
                <div style={{ fontFamily: 'var(--mono)', fontSize: 13, color: 'var(--t-3)', marginBottom: 6 }}>Inharmonic energy</div>
                <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>13.6 dB less</div>
              </div>
            </div>
            <p style={{ color: 'var(--t-3)', fontSize: 14, lineHeight: 1.6, maxWidth: '64ch' }}>
              Reproducible, from the test suite, not from a listening session. Oversampling reduces
              this — it does not abolish it, and anyone who tells you otherwise has not measured it.
            </p>

            <div style={{ marginTop: 26, display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 28 }}>
              <div>
                <h4 style={{ fontSize: 15, marginBottom: 6 }}>2x and 4x, and 1x is still there</h4>
                <p style={{ color: 'var(--t-2)', lineHeight: 1.6, fontSize: 15 }}>
                  1x is the original behaviour, it is a sound, and someone will want it. It is not
                  the default.
                </p>
              </div>
              <div>
                <h4 style={{ fontSize: 15, marginBottom: 6 }}>The latency is declared</h4>
                <p style={{ color: 'var(--t-2)', lineHeight: 1.6, fontSize: 15 }}>
                  Exactly half the filter length, reported to the host, so your channels stay
                  lined up. We tell you the number rather than pretending there isn't one.
                </p>
              </div>
              <div>
                <h4 style={{ fontSize: 15, marginBottom: 6 }}>Linear phase, stated stopband</h4>
                <p style={{ color: 'var(--t-2)', lineHeight: 1.6, fontSize: 15 }}>
                  A polyphase halfband filter, whose coefficients are derived at start-up rather
                  than copied from a table — so the stopband is a number we can quote.
                </p>
              </div>
            </div>
          </div>

          <div style={{ marginTop: 52, paddingTop: 40, borderTop: '1px solid var(--line-strong)' }}>
            <h3 style={{ fontSize: 20, marginBottom: 10 }}>The stereo stage leaves your low end alone</h3>
            <p style={{ color: 'var(--t-2)', lineHeight: 1.7, maxWidth: '64ch' }}>
              Below about 150 Hz the ear places a sound by level and timing, not by the
              channel-to-channel relationship a widener manipulates — and a room's low end is
              ruled by its modes, not by where the sound is. So widening the bass buys no image at
              all, and it costs you the weight of the mono sum. It is the most common single reason
              a mix that sounds enormous on headphones falls apart on a club system, a phone, or a
              record.
            </p>
            <p style={{ color: 'var(--t-2)', lineHeight: 1.7, maxWidth: '64ch', marginTop: 14 }}>
              So the stage splits the signal, widens only what is above the crossover, and returns
              the low band untouched. The crossover is fixed and has no control, deliberately: you
              cannot hear a crossover, only what it protects, so a knob here would be one nobody
              could set by ear. It is a gentle one-pole, chosen so the two bands sum back to the
              input when the widener is doing nothing at all.
            </p>
          </div>

          <div style={{ marginTop: 52, paddingTop: 40, borderTop: '1px solid var(--line-strong)' }}>
            <h3 style={{ fontSize: 20, marginBottom: 10 }}>It stays cheap in the quiet parts</h3>
            <p style={{ color: 'var(--t-2)', lineHeight: 1.7, maxWidth: '64ch' }}>
              Every filter on the desk decays toward zero when the music stops, and lands in the
              denormal numbers below the float format's normal range — where some processors run
              one to two orders of magnitude slower. The symptom is a plugin that is cheap while
              playing and expensive in the gaps, which is exactly the wrong way round: the gaps are
              where forty channels of reverb tail have nothing to hide behind. It never shows up on
              a CPU meter, because you read that while something is playing. One register write per
              block fixes it, and the host's own setting is put back on the way out.
            </p>
          </div>
        </Section>

        {/* ---- The planned voices -----------------------------------------
             Live as of 2026-09-04. It was behind ?voices=on while two of the
             nine renders were unusable - one carried a real manufacturer's
             badge, one had a typo on its nameplate. Both were retouched and
             checked, so the gate is gone.

             The framing has NOT changed and must not: these are nine PLANNED
             voices for an unreleased product. Artwork existing does not make
             any of them exist. */}
        <ConsoleVoices />

        {/* ---- Metering spec ---------------------------------------------- */}
        <Section id="metering">
          <Eyebrow>Metering</Eyebrow>
          <h2 style={{ fontSize: 'clamp(26px, 3.4vw, 38px)', letterSpacing: '-0.03em', marginBottom: 8, maxWidth: '24ch' }}>
            Calibrated, and checked at five sample rates.
          </h2>
          <p style={{ color: 'var(--t-2)', maxWidth: '62ch', marginBottom: 20, lineHeight: 1.65 }}>
            A meter is only worth having if you can trust the number on it. Here is exactly what
            was verified, and by what.
          </p>

          <Spec label="Analog VU" value="IEC 60268-17. 300 ms to 99 per cent, 1.25 per cent overshoot. 0 VU referenced to −8, −14, −18 or −20 dBFS." />
          <Spec label="PPM" value="DIN 45406, BBC IIa, and EBU/Nordic IIb, with the ballistic constants derived per type rather than shared." />
          <Spec label="True peak" value="Sample peak plus 4x polyphase inter-sample detection. Worst case reads 0.026 dB low on a crest that falls exactly between two samples." />
          <Spec label="Loudness" value="Implements ITU-R BS.1770-4 momentary, short-term and integrated, with EBU Tech 3342 loudness range, verified against the standard's own formula." />
          <Spec label="Correlation" value="Correlation and balance on the first channel pair, checked against identical, inverted and asymmetric material." />
          <Spec label="Sample rates" value="Calibrated at 44.1, 48, 88.2, 96 and 192 kHz — not at one rate and assumed for the rest." />
          <Spec label="The metering path" value="Bit-transparent. Verified through the plugin's own C ABI with separate input and output buffers, so it is a measurement rather than an intention." />
          <Spec label="Cost on the audio thread" value="64 tracks at 48 kHz measured at 12.5 per cent of one core." />
        </Section>

        {/* ---- What it does not do ---------------------------------------- */}
        <Section id="limits" tone="sunk">
          <Eyebrow>What it does not do</Eyebrow>
          <h2 style={{ fontSize: 'clamp(26px, 3.4vw, 38px)', letterSpacing: '-0.03em', marginBottom: 8, maxWidth: '24ch' }}>
            The part most pages leave out.
          </h2>
          <p style={{ color: 'var(--t-2)', maxWidth: '62ch', marginBottom: 24, lineHeight: 1.65 }}>
            You will find all of this out in the first ten minutes anyway. Better here.
          </p>

          <Spec
            label="Loudness conformance"
            value={<>The loudness engine is checked against the standard's formula. It has <strong>not</strong> been run against the EBU Tech 3341 conformance material, which is a different and stricter thing. Until that run happens we will not describe it as anything more than what it is.</>}
          />
          <Spec
            label="VCA groups"
            value={<>REAPER has had these natively since version 5. What is on the roadmap is showing them properly, because they are awkward and invisible today. Presentation, not a new capability — the same goes for track colour on the name plate.</>}
          />
          <Spec
            label="Channel count"
            value="Two. The analysis plugin declares a stereo port; a track carrying more is metered on its first two channels." />
          <Spec
            label="Platform"
            value="macOS, and inside REAPER. There is no build for a second operating system yet, and nothing is code-signed or notarised." />
          <Spec
            label="Availability"
            value="Not released. There is no price, no download and no licence to buy. When any of that changes it will change here first." />
        </Section>

        {/* ---- Close ------------------------------------------------------ */}
        <Section id="notify">
          <div style={{ textAlign: 'center', maxWidth: 640, margin: '0 auto' }}>
            <h2 style={{ fontSize: 'clamp(24px, 3vw, 34px)', letterSpacing: '-0.03em', marginBottom: 14 }}>
              Want to know when it is ready?
            </h2>
            <p style={{ color: 'var(--t-2)', lineHeight: 1.7, marginBottom: 26 }}>
              Send us a line and we will tell you when there is something to install. No list you
              have to escape from afterwards.
            </p>
            <a href="../../contact/" className="btn-primary">Get in touch</a>
            <p style={{ color: 'var(--t-3)', fontSize: 14, marginTop: 30, lineHeight: 1.6 }}>
              Meanwhile, <a href="../../" style={{ color: 'var(--o-dim)', textDecoration: 'underline' }}>Virtue FX Manager</a> is
              shipping, and it is the other half of the same idea: your tools where you can see them.
            </p>
          </div>
        </Section>

      </main>

      <SiteFooter base="../../" />
    </>
  );
};

const rootC = ReactDOM.createRoot(document.getElementById('root'));
rootC.render(<Console />);
