/* Reader-controlled text size. Every authored size on the site is calc(<len> * var(--ts)),
   so this one number is the whole control; type-scale.js writes it to <html> and the inline
   snippet in each page's <head> applies the stored value before first paint.
   Declared outside the theme blocks on purpose: light and text size are independent choices, and
   a value living in :root[data-theme="day"] would be reset every time the paper changed. */
:root{ --ts:1 }


:root{
  
  --paper-day:#FBFAF8;
  --paper-dusk:#E2DED4;
  --paper-night:#0D141D;

  --bg:var(--paper-day);
  --ink:#191917;
  --body:#33322E;
  --muted:#6E6B64;
  /* --faint carries real text (stat captions, the "01 · Models" eyebrows, the /mo price
     units, the tier descriptors) and the text-size control's inactive glyphs, so it has to
     clear WCAG AA 4.5:1 against BOTH --bg and the tinted --tier-hi card. The old values
     (#9B978E day, #78746A dusk, #5C6B78 night) sat at 2.68-3.53:1. Each of these is the
     original hue and saturation walked to the nearest passing lightness, which keeps it a
     step fainter than --muted rather than collapsing the two. */
  --faint:#736F66;   /* 4.80:1 on --bg, 4.60:1 on the tinted card */
  --rule:#E5E2DA;
  --rule-strong:#D2CEC4;
  /* The resting edge of the CTA card, and the only edge it has once motion is turned down.
     --rule is a hairline meant for dividers between rows of text and measures 1.24:1 on paper;
     on a 780px frosted card with a 22px radius, floating over the wave field, it read as no
     edge at all. This is the paper's own hue with a slightly lifted chroma (C 0.018, roughly
     1.6x --rule's, so it keeps the relationship that token already has to its paper), set to a
     constant OKLab dL of 0.27 from the paper in all three themes.

     dL, not the contrast ratio, WAS held constant across the themes, on the argument that
     WCAG's luminance ratio predicts appearance poorly in the light-on-light regime this edge
     lives in. That argument is sound for choosing between two passing values and useless for
     clearing a floor: a constant dL of 0.27 put the three ratios at 2.42-2.59:1, every one of
     them under the 3:1 SC 1.4.11 requires of a non-text boundary. The floor is not negotiable
     and the appearance match is, so L is now solved per theme for the RATIO -- 3.03/3.04/3.05:1
     -- and dL merely lands close (0.307-0.330). Only OKLab L moved; each theme keeps its hue.

     This matters most for the readers who never see the ring. Both reduced-motion kills are
     display:none, so at 'reduced' or 'off' this edge is the only thing bounding the card, and
     the cohort that asked for less motion was the one cohort getting the weakest boundary.

     Rejected: deriving it as the OKLab mean of the four pillars, so the resting state would
     literally be "the four colours at rest". That works on night, where the mean is essentially
     neutral, but the four hues are not evenly enough spread for their vector sum to be neutral
     on the light papers -- the day mean lands at H 298.6 C 0.026, a violet-grey that fights the
     warm paper. Paper hue is the honest choice.

     Ceiling is about 3.2:1 (dL 0.34, #928D82) if this ever needs to go further -- there is only
     that sliver of headroom left above the current 3.03. Past it a 1px rim on a radius this
     large stops reading as an edge and starts reading as a drawn box, which is the thing the
     deeper border exists to avoid rather than to cause. */
  --rule-deep:#949185;   /* 3.03:1 on --bg; --rule is 1.24:1 and --rule-strong 1.51:1 */
  /* Nudged from #0F7F66: on --mint-soft (the "New" pill) it measured 4.35:1. Same hue, and it
     still clears on paper, so links are unchanged to the eye. */
  --mint:#0E7A62;
  /* Text painted ON a --mint fill (the tier badges). It has to flip with the theme: day and
     dusk mint is dark so white reads, but night mint is light, where white would be 1.90:1. */
  --on-mint:#FFFFFF;
  --mint-soft:#E8F3EF;
  --hl:#FFE1A8;            
  
  --on-accent:#0D141D;
  --on-hl:#191917;
  --amber:#8A5A0F;
  --amber-soft:#F7E9CE;
  --amber-rule:#E7D3AA;

  /* ---- pillar accents -------------------------------------------------------------------
     One hue per product, shared with app.css so a label means the same thing on the homepage
     and on the page it links to. Every label that names a pillar wears one: the router cards
     in the hero, the .cap headers in the showcase, the eyebrows on the sub-pages.

     Text tokens, so these are the AA-tuned values -- they carry 11-15.5px uppercase labels,
     which is small text at 4.5:1, not the 3:1 a fill would want. Measured against --bg. */
  --p-models:#1F5FA8;    /* 6.17:1 on --bg */
  --p-context:#0C6E58;   /* 5.94:1 -- a shade off --mint so a label is not mistaken for a link */
  --p-gov:#8A5A0F;       /* 5.67:1 -- the same value as --amber, which is where it came from */
  --p-docs:#9A3D8F;      /* 5.86:1 */

  /* ---- the announcement capsule's surface ------------------------------------------------
     The fourth member of the soft-surface family (--mint-soft, --amber-soft, --tier-hi), and
     the only one that is a PLATE rather than a badge tint. It exists because .ctapill -- the
     hero capsule that names the newest model release and links to /models/ -- was the one
     component on the homepage a reader's eye skipped over.

     Derived, not eyeballed: --p-models' own OKLab hue held exactly (254.6 day / 255.4 dusk /
     247.7 night), chroma fixed at 0.055 in all three, and LIGHTNESS solved per theme so the
     plate sits a constant OKLab dE of 0.105 from the frosted .ctahero panel it lies on --
     0.1049 / 0.1042 / 0.1046. Measured luminance lift 1.32 / 1.31 / 1.31 over that panel.

     Why dE against the PANEL and not lightness against the paper, which is how --mint-soft was
     built: --mint-soft is a badge tint sitting on paper and separates from it by dE 0.0332 on
     day; the old pill surface, rgba(--surface,.72) composited over the panel, separated by
     0.0450. Both sit in the same band, and that band is precisely what "this section isn't
     highlighted" describes -- a tint, not a plate. 0.105 is not invented either: it is what
     NIGHT's --mint-soft already does against night paper (0.1038), and night's soft surfaces
     are the ones that read as objects. Day and dusk now get the step night always had.

     Solved for dE rather than for a constant dL, and the reason is the one already written up
     on --rule-deep: a constant dL across three papers put three ratios in three different
     places. Holding dE lands the three lifts at 1.32/1.31/1.31, near-identical by construction.

     REJECTED: matching --mint-soft's ABSOLUTE lightness (day L 0.955, i.e. #C9E0FF at this
     hue). It measures dE 0.056 and 1.07:1 -- LIGHTER than its own ground, so it reads as a hole
     punched in the frost rather than a card laid on it. That is the current failure with a blue
     cast on it.
     REJECTED: buying the step from chroma alone at the family lightness. sRGB will not sell it:
     the gamut ceiling at H 254.6 / L 0.90 is C 0.049, which tops out at dE 0.056.

     These two live in home.css only, because .ctapill is homepage-only -- grepped, index.html
     is the sole match in public/. If the capsule pattern is ever reused on a product page,
     MIRROR BOTH TOKENS INTO app.css's three theme blocks FIRST: an unresolvable var() is
     invalid-at-computed-value-time, so background falls to transparent and border-color falls
     to currentColor, and the pill would silently render as roughly the thing this whole change
     exists to fix. */
  --p-models-soft:#B1CBEC;   /* L 0.8333 C 0.055 H 254.6 -- dE 0.1049 from the frosted panel */
  /* Text painted ON a --p-models fill: the NEW chip at rest, and the whole capsule on hover.
     It flips with the theme for exactly the reason --on-mint does -- day and dusk --p-models
     are dark so white reads, night --p-models is #9BC9F5, a LIGHT blue, where white measures
     1.74:1. Dusk is left to INHERIT this white, the same way it inherits --on-mint, because
     dusk --p-models is darker than day's and white gets easier there, not harder (7.96:1). */
  --on-p-models:#FFFFFF;     /* 6.44:1 on --p-models */

  /* ---- an alternate accent for the same capsule, used when it is not announcing the model.
     Not the dE-matched derivation two blocks up -- that took an OKLab solve per theme, and this
     pill changes what it points at more often than that is worth redoing. The plate blends the
     pillar hue into the page's own opaque --bg at compute time instead, the same technique
     fact.css and agent-pill.css already use elsewhere in this file's family; it is verified for
     CONTRAST, not for perceptual distance from the panel. --on-p-context follows --on-p-models'
     own logic exactly: day/dusk --p-context is dark enough for white, night's is a light teal
     and needs the dark ink instead (set in the night block below). */
  --p-context-soft:color-mix(in srgb, var(--p-context) 16%, var(--bg));
  --on-p-context:#FFFFFF;     /* 6.20:1 day, 7.95:1 dusk (inherited) */

  /* ---- the same four hues again, as ring stops ------------------------------------------
     Fills for the spinning border on the CTA card. They had to be re-derived rather than
     reused, and the reason is the property that makes --p-* good text tokens: all four were
     walked to the same narrow contrast band (5.42-6.17:1 here, 8.06-10.63:1 on night), which
     leaves them near-ISOLUMINANT. Lightness is the one channel a dichromat keeps whole, so
     isoluminance is exactly what destroys a colour cycle for a colour-blind reader -- built on
     the raw --p-* values the best available ring measured 7.8 deuteranopic against a floor of
     8, i.e. a uniform band that never appears to change colour as it turns.

     These hold each pillar's hue exactly (max drift 0.5deg, so the ring still reads as the
     product colours) and buy the separation instead with a small alternating LIGHTNESS stagger
     and a modest chroma gain: +/-0.02 and 1.20-1.35x on day and dusk, +/-0.06 and 1.30x on
     night. Worst adjacency anywhere in the three themes is 18.1 normal-vision and 11.1 under
     the pessimistic of two published CVD simulators, against floors of 15 and 8.

     The stagger was MEANT to alternate around the cycle, so a dichromat would still get a
     bright-dark-bright-dark rhythm sweeping past the corner. Measured, it does not, and the
     theme it fails on is DAY -- the default. Simulating the four in cycle order (Machado 2009,
     severity 1.0) gives OKLab L of 0.551/0.505/0.493/0.471 under protanopia: a near-monotone
     ramp with a single step at the gov->models seam, not an alternation. Deuteranopia gives
     0.502/0.475/0.551/0.512, where gov and models sit 0.010 apart and collapse together. Night
     is the only theme where the rhythm is real at a perceptible size (deuteranopic adjacent
     gaps to 0.097); dusk alternates arithmetically but by 0.002-0.006, which is nothing.

     So, honestly: on night the ring reads as MOVING without reading as coloured; on day and
     dusk a dichromat gets about one bright-then-dark sweep per revolution instead of two. A
     weaker cue than intended, not an absent one -- and the ring encodes NOTHING, being
     decoration on a card whose four products are each named in text directly below it, so what
     is lost is the rhythm and not any information. Widening the day stagger would fix it and
     would cost the narrow equal-contrast band these values were derived to hold; that trade
     has not been made.

     The cycle ORDER is not free either, and it is not the one you would guess -- see the
     gradient in motion.css, which is the only place these are used. */
  --b-models:#1168C2; --b-context:#006D56; --b-docs:#AE3AA1; --b-gov:#8A5800;

  --tier-hi:rgba(232,243,239,.5);
  --surface:251,250,248;   
  
  --wave-line:25,25,23;
  /* ---- the accent strand, one triplet per product -----------------------------------------
     RGB triplets rather than hexes because waves.js composes the rgba() itself. --wave-accent
     below simply points at one of them, and on the product pages app.css repoints it off a
     data-pillar attribute -- that indirection is the whole of the per-product recolour.

     The homepage never repoints it: this page is not a product page and keeps the context
     green it has always had. These live here anyway because home.css and app.css maintain the
     token block twice at identical values, and a half-mirrored set fails SILENTLY -- an
     unresolvable var() makes getPropertyValue return '', waves.js falls back to its hard-coded
     '15,127,102', and the page looks exactly like a deploy that did not happen.

     Derivation and the honest limits of it are recorded once, in app.css, where the routing
     lives. --wave-context is byte-for-byte the value that has always been here. */
  --wave-models:12,107,202;    /* #0C6BCA */
  --wave-context:15,127,102;   /* #0F7F66 -- unchanged */
  --wave-gov:148,96,12;        /* #94600C */
  /* Declared ONCE, here. There is no per-product routing on this stylesheet at all: home.css
     is loaded only by index.html, which carries no data-pillar, so --wave-accent here always
     resolves to context. These triplets exist so the homepage and the product pages stay in
     one palette, not because anything on this page switches between them. The routing, and
     the source-order invariant that protects it, live in app.css beside :root[data-pillar].
     An earlier note here claimed a theme declaration would "break the product pages" -- it
     could not, since the product pages never load this file. */
  --wave-accent:var(--wave-context);
  /* ---- the folded state (motion: off) -------------------------------------------------
     A faint ground for the bars to stand on, so the folded field reads as a panel rather than
     as marks floating on the paper. Deliberately FAINT.

     The first attempt at this went the other way: a .72 scrim with the strokes crossing to a
     light palette, on the theory that `lighter` compositing needs a dark ground to work
     against. It does, but the hero card on this page is translucent, so a heavy scrim came
     up through it and turned the card and the copy inside it grey. The bars were legible and
     everything in front of them was dirty.

     Dark ink on a light ground is what these themes are for, and it composites fine: `lighter`
     accumulates, so the strokes only ever add density, and overlapping bars get darker rather
     than washing out. So the ink stays exactly as it is and this does nothing but give it a
     field. Weight, not colour, is what makes the bars read here (see barW in waves.js).

     Night sets --wave-scrim-a:0: it is already dark and already reads. */
  --wave-scrim:20,24,30;
  --wave-scrim-a:.14;
  --wave-alpha:1;   /* was 1. Day and dusk ran the field at full strength while night was
                        already restrained to .55; on the light paper that is what made the
                        hero read as busy, and it is the same field the frosted plates below
                        exist to defend the copy from. */
  
  --sans:"Inter","SF Pro Text",-apple-system,BlinkMacSystemFont,"Segoe UI Variable Text","Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;

}

:root[data-theme="dusk"]{
  --bg:var(--paper-dusk);
  --surface:226,222,212;
  --rule:#CFCABC;
  --rule-strong:#B6B0A0;
  --rule-deep:#857D69;   /* 3.04:1 on dusk paper -- dusk's own hue, L solved for the ratio */
  --muted:#57544C;
  --faint:#636057;   /* 4.68:1 on --bg, 4.76:1 on the tinted card */
  /* Dusk inherited --mint and --amber from the day :root but paints them on darker paper, so
     every link on the page sat at 3.65:1 and the amber labels at 4.37:1. Same hues, walked to
     the nearest passing lightness against dusk's page, mint-soft and amber-soft surfaces. */
  --mint:#0D6D57;    /* 4.64:1 worst case, was 3.65:1 */
  /* The binding constraint used to be the .beta badge, which painted a 13% wash of --amber over
     dusk paper and so darkened with the token: 4.67:1 there, 5.59:1 on plain paper. The badge is
     gone (2026-09-07); the value is kept because other amber-on-paper text inherits the margin. */
  --amber:#754C0D;
  --mint-soft:#D4E5DD;
  --amber-soft:#EBDCBD;
  --amber-rule:#D3BD96;
  --tier-hi:rgba(214,229,221,.55);
  /* Same hues on dusk paper, nearest passing lightness. The day values fall to 4.40-4.79:1
     here, which puts --p-gov under AA on the page and all four under it on the tinted card.
     These measure 5.42-5.92:1 on --bg and 4.87:1 worst case on --card2. */
  --p-models:#1B5192; --p-context:#0A5C49; --p-gov:#754C0D; --p-docs:#8A3480;
  /* Same construction as day: --p-models' hue held, C 0.055, L solved for a dE of 0.1042 from
     the composited dusk panel (#CFCCC3). Lift 1.31:1, and 1.56:1 over bare dusk paper.
     --on-p-models is deliberately not restated here; see the note in the day block. */
  --p-models-soft:#9CB5D6;   /* L 0.7642 C 0.055 H 255.4 -- dE 0.1042 */
  /* Ring stops on dusk paper. Same construction as day: pillar hues held exactly, +/-0.02
     lightness stagger, 1.20-1.35x chroma. Worst adjacency 18.1 normal / 12.9 CVD. */
  --b-models:#005AB3; --b-context:#005B47; --b-docs:#A52698; --b-gov:#754A00;
  /* Dusk deliberately sets no --wave-* triplets. The dusk pillar tokens differ from the day
     ones only by an AA lightness walk onto darker paper, which is a constraint on TEXT; the
     canvas is not text and inherits the day hues. Deriving from dusk's own hues instead lands
     3/255 away, under a JND and not worth a block. The measured cost is that the dusk field is
     13% quieter than day (weight dE 3.20 vs 3.55) because dusk paper is lighter-adjacent -- a
     deficit that already existed for the green and is left alone. If parity is ever wanted the
     lever is --wave-alpha, NOT the accent: deepening lightness would break the equal-weight
     family that keeps the three product fields the same visual size. */
}

:root[data-theme="night"]{
  --bg:var(--paper-night);
  --surface:13,20,29;
  --ink:#E8EEF3;
  --body:#CFD8E0;
  --muted:#8593A1;
  --faint:#7B8C9A;   /* 5.34:1 on --bg, 4.57:1 on the tinted card */
  --rule:#243040;
  --rule-strong:#33445A;
  --rule-deep:#4E6480;   /* 3.05:1 on night paper -- night's own hue, L solved for the ratio */
  --mint:#5FCFB4;
  --on-mint:#0D141D;   /* 9.75:1 on night mint */
  --mint-soft:#16302B;
  --amber:#E3A049;
  --amber-soft:#33270F;
  --amber-rule:#5A4520;
  /* Inverted for dark paper: the day values measure 2.87-3.13:1 here. These are 8.06-10.63:1
     on --bg and 6.60:1 worst case on --card2. */
  --p-models:#9BC9F5; --p-context:#5FCFB4; --p-gov:#E3A049; --p-docs:#DE93CC;
  /* Goes DARK, the way --mint-soft #16302B does, but LIFTS off the paper rather than sinking
     into it: night's soft tokens sit ABOVE night paper (--mint-soft is +0.098 L). L 0.2871
     C 0.055 H 247.7 -- dE 0.1046 from night paper, which IS the panel here since night sets
     --wave-scrim-a:0, and a 1.31:1 lift. It lands 0.0005 L from --mint-soft's 0.2866, so the
     night soft family stays isoluminant across products exactly as it already was; only the
     hue moved. Under both dichromacies the two therefore measure 1.01:1 against each other BY
     CONSTRUCTION and separate on hue instead -- dE 0.0515 deutan, 0.0487 protan, both about
     2.5x JND. A night reader with either dichromacy can tell a models surface from a context
     surface; they cannot tell them apart by lightness, and were never meant to. */
  --p-models-soft:#112D44;
  --on-p-models:#0D141D;   /* 10.63:1 on night --p-models; white here would be 1.74:1 */
  --on-p-context:#0D141D;   /* 9.75:1 on night --p-context, itself a light teal */
  /* Ring stops on night paper. Night needed a bigger stagger (+/-0.06) and 1.30x chroma:
     the night pillar tokens sit in an even narrower band than the light ones, 8.06-10.63:1,
     so there was less lightness variation to start from. Worst adjacency 20.4 normal /
     11.1 CVD, and the deutan lightnesses still alternate 0.859/0.702/0.802/0.681. */
  --b-models:#ACD6FF; --b-context:#00BA9B; --b-docs:#F996E2; --b-gov:#CF8500;
  --wave-line:232,238,243;
  /* Night retunes the triplets and leaves --wave-accent alone -- see the note on that
     declaration in the day block for why a --wave-accent here would break the product pages.
     --wave-context is unchanged; the other two are the night anchor's lightness and chroma at
     each pillar's hue. --wave-models is NOT --p-models: that token measures 14.5 from the
     white strand waves.js draws every eighth line in, under the floor of 15, so a /models/
     field built on it would have collapsed its two strand populations into one pale wash.
     #7DBEFB measures 19.3. */
  --wave-models:125,190,251;   /* #7DBEFB */
  --wave-context:95,207,180;   /* #5FCFB4 -- unchanged, and the anchor the other two derive from */
  --wave-gov:229,171,101;      /* #E5AB65 */
  --wave-alpha:.55;
  --wave-scrim-a:0;   /* already dark: the bars need no ground of their own */
  
  --tier-hi:rgba(95,207,180,.09);
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--body);
  font-family:var(--sans);font-size:calc((16.5px) * var(--ts));line-height:1.58;letter-spacing:-.005em;
  -webkit-font-smoothing:antialiased;overflow-x:clip;
}
a{color:var(--mint);text-decoration:none}
::selection{background:var(--mint);color:var(--on-mint);text-shadow:none}
::-moz-selection{background:var(--mint);color:var(--on-mint);text-shadow:none}
a:hover{text-decoration:underline;text-underline-offset:3px}

/* Skip link. Off-canvas rather than display:none or width:0, because a hidden element is not
   focusable and the whole point is that the first Tab reaches it. It targets <main tabindex="-1">
   so focus actually moves; without the tabindex the page scrolls but focus stays in the nav. */
.skip{
  position:absolute;left:-9999px;top:0;z-index:120;
  font-family:var(--sans);font-size:calc((14.5px) * var(--ts));font-weight:600;
}
.skip:focus{
  left:12px;top:12px;padding:10px 16px;border-radius:8px;
  background:var(--bg);color:var(--ink);border:1px solid var(--rule-strong);
  text-decoration:none;outline:2px solid var(--mint);outline-offset:2px;
}
main:focus{outline:none}   /* the skip target takes focus programmatically; no ring needed */

/* Visually hidden but still announced. Used to qualify the four "Learn more" links, which a
   screen reader lists out of context and which are otherwise indistinguishable from each other. */
.vh{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}

.wrap{max-width:1180px;margin:0 auto;padding:0 28px;position:relative;z-index:1}

[id]{scroll-margin-top:86px}
html{scroll-behavior:smooth}

/* 100%, not 104vw. The field used to be 4% wider than the viewport and centred, so it hung ~26px
   past each edge (which put the page's scrollWidth 26px beyond its clientWidth on every visit).
   Chrome never scrolled: `overflow-x:clip` on body propagates to the viewport, which clips it. But
   the clip is the only thing holding it, so any engine without `clip` (Safari before 16) DID
   scroll sideways by 26px on a marketing page, and every audit tool reports the overflow whether
   it scrolls or not.

   100% is the BODY's width, not 100vw (vw includes the scrollbar and would put 15px back).
   Nothing is lost visually: the strands are diagonal ribbons that run off both edges anyway, so
   the 4% of slack was only ever hiding the ends of curves that leave the frame regardless. */
.flow{
  position:absolute;top:0;left:0;
  width:100%;height:min(150vh,1180px);
  z-index:0;pointer-events:none;
}

@media(max-width:560px){
  /* Same reasoning as the subpages: a label longer than the screen should wrap, not push. */
  .btn{white-space:normal;text-align:center}
  /* The billing toggle is one inline-flex row; at the largest text size its "2 months free"
     badge alone reached 350px. Let the two options stack instead. */
  .terms{flex-wrap:wrap;max-width:100%}
  .terms button{flex:1 1 auto;justify-content:center}
}
.foot a{
  font-family:var(--sans);font-size:calc((16px) * var(--ts));color:var(--body);
  padding:5px 11px;border-radius:7px;
  background-image:linear-gradient(var(--hl),var(--hl));
  background-size:0% 100%;background-repeat:no-repeat;background-position:left center;
  transition:background-size .22s cubic-bezier(.22,.61,.36,1),color .22s;
}
.foot a:hover{background-size:100% 100%;text-decoration:none;color:var(--on-hl)}

/* Appearance popover. One 38px button in place of two 104px pills, which is what stopped the
   header wrapping to two rows the moment the reader turned text size up. Fixed 38px on
   purpose: it is an icon, and the cluster around it already grows with --ts. */
.appearance{position:relative;flex:none}
.appearance-btn{
  display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;padding:0;
  border:1px solid var(--rule-strong);border-radius:9px;background:transparent;cursor:pointer;
  color:var(--ink);transition:border-color .2s,color .2s;
}
.appearance-btn:hover{border-color:var(--ink)}
.appearance-btn:focus-visible{outline:2px solid var(--mint);outline-offset:2px}
.appearance-btn svg{display:block}
.appearance-panel{
  position:absolute;top:calc(100% + 8px);right:0;z-index:80;
  display:flex;flex-direction:column;gap:14px;
  padding:16px;border-radius:12px;
  /* Mixing --ink into --bg lifts the panel off the page in BOTH directions without a second
     token: --ink is dark on light paper and light on dark, so this darkens the panel in day
     and lightens it at night. A flat var(--bg) matched the page exactly, and at night the
     drop shadow is black on near-black, so the only thing separating the two surfaces was a
     1.86:1 border. */
  background:color-mix(in srgb,var(--ink) 5%,var(--bg));
  border:1px solid var(--rule-strong);
  box-shadow:0 16px 40px rgba(0,0,0,.22);
}
@supports not (background:color-mix(in srgb,red,blue)){
  .appearance-panel{background:var(--bg)}
}
.appearance-panel[hidden]{display:none}
/* --faint was tuned to clear 4.5:1 against --bg and the tinted card, and the panel is neither:
   lifting its surface off the page dropped the inactive glyphs to 4.35:1. --muted has the
   headroom, and inside a panel that is already a deliberate surface the extra weight is right. */
.appearance-panel .themeswitch button{color:var(--muted)}
/* flex-start, not the default stretch: every row used to be exactly as wide as the 104px
   pill it held, so the two were indistinguishable. The motion row can now also hold a line
   of explanation, and under stretch that widened the pills above it into long rounded
   troughs with three small glyphs huddled at the left end. The pill should be its own size
   whatever is sitting under it. */
.ap-row{display:flex;flex-direction:column;align-items:flex-start;gap:7px}
.ap-lbl{
  font-family:var(--sans);font-size:calc((11.5px) * var(--ts));font-weight:600;
  letter-spacing:.12em;text-transform:uppercase;color:var(--muted);
}
/* Shown only when the page stood its own motion down, to say so.
   An explicit WIDTH, not a max-width. The panel is absolutely positioned and sizes to its
   content, and inside a column flex container a paragraph's max-width does not widen the
   box (the row is sized by the 104px pill above and the sentence then wraps to that,
   seven words tall). A width participates in the intrinsic sizing, so the panel opens to
   fit the note and closes back to the pills when there is no note to show.
   Scaled by --ts with everything else on the site, which holds the measure at roughly the
   same number of words per line however large the reader has asked for the text. */
.ap-note{
  margin:0;width:calc(212px * var(--ts));
  font-family:var(--sans);font-size:calc((12px) * var(--ts));line-height:1.42;color:var(--muted);
}
.ap-note[hidden]{display:none}
/* The way back to auto, offered only once the reader has overridden something. A text
   button rather than a fourth pill position: handing the decision back is not a LEVEL, and
   it would not have been comparable to the three it sat beside. */
.ap-reset{
  padding:0;border:0;background:none;cursor:pointer;text-align:left;
  font-family:var(--sans);font-size:calc((11.5px) * var(--ts));line-height:1.3;
  color:var(--muted);text-decoration:underline;text-underline-offset:2px;
}
.ap-reset:hover{color:var(--mint)}
.ap-reset:focus-visible{outline:2px solid var(--mint);outline-offset:2px;border-radius:3px}
.ap-reset[hidden]{display:none}
@media(max-width:560px){
  /* Anchored right, it would hang off the left edge of a small phone. */
  .appearance-panel{right:auto;left:0}
}

.hero{padding:56px 0 0;position:relative;text-align:center}
.wordmark{display:flex;align-items:center;justify-content:center;
  gap:clamp(14px,2.2vw,26px);margin:0}
.herologo{
  display:block;flex:none;
  
  width:min(clamp(52px,9vw,112px),14vw);height:auto;
}
.wm{
  font-family:var(--sans);font-weight:700;color:var(--ink);
  /* min(..., 13vw) is the guard rail. The reader's text scale multiplies every size on the
     site, and on a 320px phone at the largest step this word alone came to 300px beside a
     52px logomark. The scale still applies right up to the point where the wordmark would
     outgrow the viewport, and then the vw term caps it. */
  font-size:min(calc((clamp(47.5px,9vw,116.5px)) * var(--ts)), 13vw);
  line-height:1;letter-spacing:-.035em;
}
.wm span{color:var(--mint)}

.pills{display:flex;flex-direction:column;align-items:center;gap:9px;margin-top:30px}
.pills .pill{margin-top:0}

.claim{font-family:var(--sans);font-weight:600;color:var(--ink);
  font-size:calc((clamp(28px,4.5vw,47.5px)) * var(--ts));line-height:1.1;letter-spacing:-.03em;margin:34px 0 0}
/* The measure is unchanged at 34em / 36em; the +40px is the plate's own padding, added so the
   TEXT still wraps where it did before there was a plate behind it. Border-box (the site default)
   is what keeps this honest on a phone: the box shrinks to the column, so a 34em cap never forces
   a 629px paragraph into a 390px screen. */
.claimsub{margin:14px auto 0;max-width:calc(34em + 40px);font-size:calc((18.5px) * var(--ts));color:var(--muted)}
.herocta{margin-top:26px}
.betanote{margin:18px auto 0;max-width:calc(36em + 40px);font-family:var(--sans);font-size:calc((14.5px) * var(--ts));
  line-height:1.6;color:var(--muted)}

/* Both hero paragraphs sit ON the moving field, and they are the only copy on the page set in
   --muted rather than --ink. Grey text over animated hairlines is the one place the waves cost
   legibility: the strands drift under the words, so the contrast behind any given letter changes
   while you are reading it. The claim above survives because it is near-black and heavy; these do
   not.

   The plate is the `.pill` treatment already used two elements up (same surface alpha, same
   blur) so this reads as the hero's existing material rather than a new one.

   No `width:max-content` here, and no content-box. Both were tried: either one pins the box to
   the 34em cap on a phone too, so a 629px paragraph sat in a 390px viewport and put the page back
   into the horizontal scroll the rule above just removed. A plain block shrinks to its container,
   and the cap is widened by the padding instead (see .claimsub / .betanote above). */
.claimsub,.betanote{
  padding:12px 20px;
  border-radius:16px;
  background:rgba(var(--surface),.72);
  -webkit-backdrop-filter:blur(8px) saturate(1.1);
          backdrop-filter:blur(8px) saturate(1.1);
}
.betanote b{color:var(--amber);font-weight:600}
.betanote a{color:var(--mint)}

/* ---- The closing ask ("Bring us one real workload."), one component on the homepage, /platform/
   and /pricing/. A dark card with a large headline, the two actions on the right and a pale tile
   mark cropped into the top corner: the only place on these pages that asks the reader to do
   something, so it is the one step in the palette allowed to be a solid plate. Layout and mark are
   the design from the preview site, rebuilt on this site's own type and tokens.

   DARK ON EVERY THEME, and that is why it declares its own ink tokens instead of reading --ink and
   --bg: those two flip with the theme (near-black text on day paper, near-white on night paper),
   so a card that borrowed them would swap its own ground and its own text whenever the paper
   changed. --cta-ink-2 is the muted line on the plate, 8.5:1 against it. Night paper (#0D141D) is
   nearly as dark as the plate, so night gets a deeper plate and a hairline edge to keep the card
   bounded; day and dusk need neither. The edge is an inset shadow, not a border, so it costs no
   layout. --cta-mark is the tile-square before the label, the night-theme amber. */
.contact{
  --cta-bg:#111613;--cta-ink:#EEF0EA;--cta-ink-2:#AAB3AB;--cta-line:rgba(238,240,234,.30);
  --cta-mark:#E3A049;--cta-edge:transparent;
  margin:34px 0 0;
}
:root[data-theme="night"] .contact{--cta-bg:#080D14;--cta-edge:var(--rule-strong)}
.contact-card{
  position:relative;overflow:hidden;isolation:isolate;
  display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);gap:40px;align-items:end;
  padding:clamp(30px,5.6vw,72px);border-radius:28px;
  background:var(--cta-bg);color:var(--cta-ink);box-shadow:inset 0 0 0 1px var(--cta-edge);
}
/* The tile mark: nine of the logo's tiles, white at 14%, its top-right corner pushed past the card's
   edge so only part of it shows. Decoration only, so it is a background rather than markup. It sits
   behind the text (z-index:-1 inside the card's own stacking context) and takes no clicks. */
.contact-card::before{
  content:"";position:absolute;right:-40px;top:-40px;width:min(420px,50%);aspect-ratio:316/234;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 316 234'%3E%3Cg fill='%23fff'%3E%3Crect x='84' y='2' width='66' height='66' rx='16'/%3E%3Crect x='166' y='2' width='66' height='66' rx='16'/%3E%3Crect x='248' y='2' width='66' height='66' rx='16'/%3E%3Crect x='2' y='84' width='66' height='66' rx='16'/%3E%3Crect x='84' y='84' width='66' height='66' rx='16'/%3E%3Crect x='166' y='84' width='66' height='66' rx='16'/%3E%3Crect x='248' y='84' width='66' height='66' rx='16'/%3E%3Crect x='84' y='166' width='66' height='66' rx='16'/%3E%3Crect x='166' y='166' width='66' height='66' rx='16'/%3E%3C/g%3E%3C/svg%3E") center/contain no-repeat;
  opacity:.14;pointer-events:none;z-index:-1;
}
.contact-k{
  display:inline-flex;align-items:center;gap:10px;margin:0 0 18px;
  font-family:var(--mono);font-size:calc((11.8px) * var(--ts));font-weight:500;letter-spacing:.12em;
  text-transform:uppercase;color:var(--cta-ink-2);
}
.contact-k::before{content:"";flex:none;width:9px;height:9px;border-radius:3px;background:var(--cta-mark)}
.contact h2{
  margin:0;max-width:20ch;font-family:var(--sans);font-size:calc((clamp(30px,4.2vw,48px)) * var(--ts));
  font-weight:600;line-height:1.08;letter-spacing:-.03em;color:var(--cta-ink);
}
.contact .sub{margin:18px 0 0;max-width:34em;font-size:calc((17px) * var(--ts));line-height:1.6;color:var(--cta-ink-2)}
.contact .ctas{justify-content:flex-start}
.contact .btn{
  min-height:50px;padding:0 22px;gap:8px;font-size:calc((16px) * var(--ts));font-weight:550;
  letter-spacing:-.005em;color:var(--cta-ink);background:transparent;border-color:var(--cta-line);
}
.contact .btn:hover{border-color:var(--cta-ink)}
.contact .btn-p{background:var(--cta-ink);border-color:var(--cta-ink);color:var(--cta-bg)}
.contact .btn-p:hover{opacity:.9}
.contact .btn svg{flex:none}
.contact .formnote{margin:16px 0 0;max-width:27em;font-size:calc((13.8px) * var(--ts));line-height:1.55;color:var(--cta-ink-2)}
.contact .formnote a{color:var(--cta-ink);text-decoration:underline;text-underline-offset:3px}
.contact a:focus-visible{outline:2px solid var(--cta-ink);outline-offset:3px}
@media (max-width:900px){
  .contact-card{grid-template-columns:1fr;gap:28px;align-items:start}
  .contact-card::before{width:min(300px,62%);right:-52px;top:-52px}
}
@media (max-width:520px){
  .contact .btn{flex:1 1 100%}
  .contact-card::before{width:min(220px,55%);right:-44px;top:-44px}
}
.formnote{font-family:var(--sans);font-size:calc((14px) * var(--ts));color:var(--faint);margin-top:10px}
.formnote a{color:var(--mint)}
.leadform label{display:block;font-family:var(--sans);font-size:calc((14.5px) * var(--ts));color:var(--muted);margin-bottom:14px}
.leadform input,.leadform select,.leadform textarea{display:block;width:100%;margin-top:6px;
  background:rgba(var(--surface),.6);color:var(--ink);border:1px solid var(--rule-strong);
  border-radius:10px;padding:11px 13px;font-size:calc((16px) * var(--ts));font-family:var(--sans)}
.leadform input:focus,.leadform select:focus,.leadform textarea:focus{outline:none;
  border-color:var(--mint);box-shadow:0 0 0 3px rgba(15,127,102,.14)}
.leadform textarea{resize:vertical}
.leadform button{border:0;cursor:pointer;font-family:var(--sans)}
.fgrid2{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}
@media(max-width:640px){.fgrid2{grid-template-columns:1fr}}

.leadform label.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.pill.next{border-color:var(--amber-rule)}
.pill.next b{background:var(--amber-soft);color:var(--amber)}
.pill{
  display:inline-flex;align-items:center;gap:10px;margin-top:30px;text-align:left;
  font-family:var(--sans);font-size:calc((15.5px) * var(--ts));color:var(--body);
  background:rgba(var(--surface),.72);backdrop-filter:blur(8px) saturate(1.1);
  -webkit-backdrop-filter:blur(8px) saturate(1.1);
  border:1px solid var(--rule);border-radius:999px;padding:7px 14px 7px 8px;
}
.pill b{
  font-family:var(--sans);font-size:calc((11px) * var(--ts));font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  background:var(--mint-soft);color:var(--mint);border-radius:999px;padding:4px 10px;
}

.glass{
  background:rgba(var(--surface),.50);
  backdrop-filter:blur(22px) saturate(1.2);
  -webkit-backdrop-filter:blur(22px) saturate(1.2);
  border:1px solid rgba(229,226,218,.85);
  border-radius:16px;
  padding:30px 32px;
}

@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass{background:rgba(var(--surface),.94)}
}

.lede{margin:40px 0 0;font-size:calc((19px) * var(--ts));line-height:1.6;color:var(--ink)}
/* Bold in prose lifts to full ink. Body copy sits on --body, so a <b> inside it was heavier
   but no darker -- the weakest of the emphasis marks the site has, and the one it reaches for
   most. Before this rule every author worked around it by hand: 22 instances of an inline
   style="color:var(--txt)" on a <b> across the four pages, the same decision made 22 times
   and available to be forgotten on the 23rd. Rules that want a different colour are more
   specific and still win. */
p b,p strong,li b,li strong,.lede b,.lede strong{color:var(--ink);font-weight:650}
.lede p{margin:0 0 20px}
.lede p:last-child{margin-bottom:0}
.lede .quiet{color:var(--muted);font-size:calc((17px) * var(--ts))}

/* Mint emphasis inside a heading: the second half of the sentence carries the point, so it gets
   the accent while the first half stays ink. Not a link, so no underline behaviour. */
.em-mint{color:var(--mint)}

/* The line under the hero buttons that answers "what does this cost me to try". */
.reassure{
  margin:14px auto 0;max-width:calc(38em + 40px);
  font-family:var(--sans);font-size:calc((14px) * var(--ts));line-height:1.6;color:var(--muted);
}

/* Section header: eyebrow, headline, one paragraph. Centred to match the hero above it. */
.secthead{max-width:calc(40em + 40px);margin:0 auto 40px;text-align:center}

/* Section labels. home.css never carried a rule for .eyebrow, so the seven labels on this page
   ("How Tileward works", "Economic outcomes", "Proof, with the limits", "Pricing" and the three
   inside the mechanism cards) rendered as ordinary 16.5px body text stacked above the heading:
   they read as a stray first line rather than as the name of the section. app.css has styled the
   same class on every subpage all along, so this is that rule, not a new invention, and a section
   label now looks the same wherever it appears. Centred inside .secthead, which is centred, and
   left-aligned in the cards, which are not. */
.eyebrow{
  display:flex;align-items:center;gap:10px;
  font-family:var(--sans);font-size:calc((13px) * var(--ts));font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--p-context);
}
.eyebrow::before{content:"";flex:none;width:22px;height:2px;border-radius:1px;background:currentColor}
/* The centred section headers put the rule mark on both sides, the way .secthead .k already does,
   so the label reads as a divider rather than as a bullet hanging off the left. */
.secthead .eyebrow{justify-content:center;margin-bottom:14px}
.secthead .eyebrow::after{content:"";flex:none;width:22px;height:2px;border-radius:1px;background:currentColor}
/* The mechanism cards carry a pillar colour on their own label. */
#reduce .eyebrow{color:var(--p-models)}
#control .eyebrow{color:var(--p-gov)}
/* The second heading inside the same showcase, over the two pieces the first two are built on.
   It needs air above it (it follows a card row rather than opening the section) and reads one
   step quieter, because the cards under it are supporting rather than the thing being sold. */
.secthead.runson{margin-top:52px;padding-top:44px;border-top:1px solid var(--rule)}
.secthead.runson .k{color:var(--muted)}
/* The section eyebrow above the four pillars. It was --amber, which is now the Governance
   accent -- one of the four things it is the umbrella over -- so it read as if the section
   belonged to that pillar. Full ink instead: it sits above all four and should not look like
   any one of them, and ink is the one value on the page that cannot be mistaken for a pillar.
   Rules on both sides because this header is centred, unlike the left-aligned .eyebrow. */
.secthead .k{
  display:flex;align-items:center;justify-content:center;gap:12px;
  font-family:var(--sans);font-size:calc((12.5px) * var(--ts));font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink);margin-bottom:14px;
}
.secthead .k::before,.secthead .k::after{
  content:"";flex:none;width:26px;height:1px;background:var(--rule-strong);
}
.secthead h2{
  font-family:var(--sans);font-weight:600;color:var(--ink);margin:0;
  font-size:calc((clamp(25px,3.4vw,34px)) * var(--ts));line-height:1.18;letter-spacing:-.025em;
}
.secthead p{
  margin:16px auto 0;font-size:calc((16.5px) * var(--ts));line-height:1.62;color:var(--muted);
}
/* THE PLATFORM HEADING IS THE HERO'S OLD SUBTITLE, moved (2026-09-06). It used to sit between
   the four router doors and this section, and motion.css + cta-motion.js grow it from .46 in
   --muted to full size in ink as it arrives -- the doors it converged from are gone, so it now
   opens the section it was always introducing, in place of the eyebrow. The size is this
   section's own (.secthead h2 outranks .ctasubtitle on font-size), and the choreography keeps
   its scale and colour transitions, which outrank this rule under html.tw-motion. What it does
   NOT keep is the 14px->26px margin-top slide: that was the gap above a free-standing line,
   and inside a padded card it read as the heading sitting low. Three classes beat the motion
   rule's two-plus-type, so the margin holds at zero in both states. */
.scenesec .secthead .ctasubtitle{margin:0 auto;padding:0;max-width:none}


/* The showcase pillar labels. Same problem as the router cards and the sub-page eyebrows:
   --faint at .1em in caps reads as faded text, and at 15.5px it was also competing with the
   19.5px heading directly under it, so the two ran together instead of reading as label and
   headline. Down to 12.5px, out to .15em, and onto the pillar accent with a rule in front of
   it -- the same treatment .eyebrow gets in app.css, so a pillar label looks the same here as
   it does on the page it links to.

   .axis-k and .tally .t-k are NOT pillars -- they label a comparison row and a tally -- so
   they keep --faint and are split off below rather than dragged along by the shared selector
   they used to sit in. */
/* The glyph is a corner watermark here, the same treatment the router cards get, so the short
   rule that used to mark this label is gone and the label is bare colour -- which is what
   .ctacard .k does too. A rule, a glyph and a label in one row were three things competing to
   be noticed.

   Bigger than the router's 58px because the cell is: these sit 2-up across the full measure,
   roughly twice a router card's width, and a mark that reads at 58px on a 257px card is a
   speck on a 530px one.

   NO overflow:hidden on .cap, deliberately. The router cards clip their watermark that way, but
   .cap contains the .fct hover notes, which are absolutely positioned and escape their line --
   clipping the cell would clip the note and silently break the one interaction in this section.
   So the mark is sized and placed to stay inside the cell on its own instead.

   right:0 lands differently in the two columns and that is wanted, not tolerated: the left cells
   carry 30px of padding-right and the right cells none, so the left mark sits inboard of the
   vertical rule rather than crowding it. */
/* The class is on the <svg> itself here, not on a wrapping span as it is in the router cards,
   so the shared `.ticon svg` rule that carries the fill and the alpha does not reach it. Both
   are restated. Missing this put an 88px glyph on the page at FULL opacity: contrast of body
   copy over it fell to 1.49:1 at night, which is unreadable, not decorative. */
/* Lift the cell's own content over the mark. Without this the watermark, being positioned,
   paints on top of the copy. */
.axis-k,.tally .t-k{
  font-family:var(--sans);font-size:calc((15.5px) * var(--ts));font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint);
}

/* A measured figure inside a sentence. Same rule app.css has carried on the subpages all along:
   the number is set apart from the prose around it and draws its underline when the card it sits
   in is hovered, so a reader scanning for the number finds it without the page shouting.

   This class used to mean the OPPOSITE here: a 43px display number inside .figs/.figbox, from a
   stat-block layout that no longer exists in any page (checked 2026-09-07, zero occurrences in
   markup or scripts). Those rules are gone. One class, one meaning, on every page. */
.fig{color:var(--ink);font-weight:650;white-space:nowrap;position:relative;
  display:inline-block;transition:color .22s ease,transform .22s ease}
.fig::after{content:"";position:absolute;left:0;right:0;bottom:-2px;height:1.5px;
  background:currentColor;transform:scaleX(0);transform-origin:left;
  transition:transform .3s cubic-bezier(.22,.61,.36,1);opacity:.5}
.card:hover .fig,.door:hover .fig,tr:hover .fig,.head:hover .fig,.path:hover .fig{
  color:var(--mint);transform:translateY(-2px)}
.card:hover .fig::after,.door:hover .fig::after,tr:hover .fig::after,
.head:hover .fig::after,.path:hover .fig::after{transform:scaleX(1)}
:where(html[data-motion="reduced"],html[data-motion="off"]) .fig,
:where(html[data-motion="reduced"],html[data-motion="off"]) .fig::after{transition:none}
.unit{font-family:var(--sans);font-size:calc((13.5px) * var(--ts));color:var(--faint);display:block;margin-top:3px}


/* ---- feature groups inside a bundle tier ------------------------------------------------
   A bundle tier lists four products' worth of allowances, and as one flat <ul> the reader had
   to work out which line belonged to which product from the wording alone. Now each product
   is its own block, keyed to the same pillar accent it wears everywhere else on the site.

   THREE channels, because colour alone would fail exactly the readers who need this most:
   the accent, the rule down the left edge (a shape, present in greyscale and forced colours),
   and the product NAME spelled out at the head of every block. Any one of the three carries it.

   Only the Everything panel uses these. A single-product ladder has nothing to group. */
/* ---- rows that line up across the ladder ------------------------------------------------
   The four groups used to be a flex column inside each tier, so every column packed its own
   content and DOCUMENTS finished at a different height in all five. A reader comparing storage
   across tiers had to find the row again in each column, which is the one job a pricing table
   exists to make easy.

   Subgrid fixes it at the layout level rather than by guessing min-heights: the tier grid owns
   six rows -- head, the four products, the CTA -- and every tier inherits them, so each product
   band starts on the same line in all five columns however the copy wraps or however far the
   reader has turned the text size up. Fixed heights in px or em cannot promise that; this can.

   The @supports fallback is the old flex column, which is untidy rather than broken. */
.tiers.grouped .tier{
  /* 1 / span 6, not a bare span: with a bare span the auto-placer gives each tier its own band
     of six rows and the whole ladder stacks into one column. Pinning every tier to rows 1-6 is
     what puts them side by side sharing the same six lines. */
  display:grid;grid-row:1 / span 6;grid-template-rows:subgrid;gap:0;align-content:start;
}
.tiers.grouped .thead{display:flex;flex-direction:column}
.fgrp{border-left:2px solid var(--acc);padding-left:9px;padding-top:9px}
@supports not (grid-template-rows:subgrid){
  .tiers.grouped{grid-template-rows:none}
  .tiers.grouped .tier{display:flex;flex-direction:column}
}
.fgrp.m{--acc:var(--p-models)} .fgrp.c{--acc:var(--p-context)}
.fgrp.g{--acc:var(--p-gov)}    .fgrp.d{--acc:var(--p-docs)}
.fgh{
  display:block;font-family:var(--sans);font-size:calc((9.5px) * var(--ts));font-weight:700;
  letter-spacing:.11em;text-transform:uppercase;color:var(--acc);margin-bottom:2px;
}
.fgrp ul{list-style:none;margin:0;padding:0}
.fgrp li{
  font-size:calc((12.5px) * var(--ts));line-height:1.45;color:var(--body);
  padding:1px 0;border:0;
}
.fgrp li b{color:var(--ink);font-weight:650}

/* The six rows a grouped tier inherits: head, Models, Context, Governance, Documents, CTA. */
.tiers.grouped{grid-template-rows:auto auto auto auto auto auto}
.tiers.grouped .tier > .btn{align-self:end;margin-top:14px}

/* ---- glyphs ------------------------------------------------------------------------------
   The same four marks the router cards wear, from one <use> sprite. A tab and the group it
   reveals carry the same shape, so the reference survives the switch: a reader who clicked the
   plug in the router lands on the plug in the tab and the plug on the Context row. */
.tico{width:13px;height:13px;flex:none;fill:currentColor;margin-right:7px;vertical-align:-2px}
.tico-all{fill:none}
.itabs button{display:inline-flex;align-items:center}
.fgi{width:9px;height:9px;flex:none;fill:currentColor;margin-right:5px}
.fgh{display:flex;align-items:center}

/* ---- the switch --------------------------------------------------------------------------
   A panel swapped with [hidden] appears instantly, which on a table this size reads as the page
   flinching rather than as one ladder replacing another. Going display:none -> block restarts a
   CSS animation on its own, so the arrival is animated with no change to tabs.js at all.

   The columns arrive in reading order. It is a short stagger, not a cascade: this sits between a
   reader and a price, and a table that takes half a second to admit what it costs reads as a
   slow site rather than a considered one. */
/* TRANSFORM ONLY, no opacity. Fading a panel in is the obvious way to write this and it strands
   the content the moment the animation does not advance: a renderer that starts the animation and
   then stops -- a backgrounded tab, a throttled compositor -- leaves the panel parked on the first
   keyframe. Observed exactly that here, opacity 0 with the animation still reporting "running",
   which is the entire price table gone. Removing fill-mode did not fix it, because a frozen
   animation never leaves its active window for the resting style to apply.

   So nothing that can hide anything is animated. The worst failure this can produce is a table
   sitting six pixels low, which nobody will ever notice and nobody can be blocked by. */
@keyframes tw-panel{ from{transform:translateY(6px)} to{transform:none} }
@keyframes tw-col{ from{transform:translateY(9px)} to{transform:none} }
/* NO fill-mode. This started as `both`, which is the usual choice for a reveal and is the wrong
   one here: `both` holds the element at the keyframe's FROM state whenever the animation is not
   advancing, and a panel that is not advancing is exactly the case that matters -- a backgrounded
   tab, a throttled renderer, a paint the browser decided to skip. Caught it stuck at opacity 0
   with the animation reporting "running": the whole price table was invisible after a tab switch.
   With no fill the resting style applies outside the animation's active window, so the worst case
   is that the panel simply appears without moving. It can never disappear. */
html.tw-motion [data-panel].tw-in{animation:tw-panel .28s cubic-bezier(.22,.85,.3,1)}
html.tw-motion [data-panel].tw-in .tier{animation:tw-col .34s cubic-bezier(.22,.85,.3,1)}
html.tw-motion [data-panel].tw-in .tier:nth-child(2){animation-delay:.04s}
html.tw-motion [data-panel].tw-in .tier:nth-child(3){animation-delay:.08s}
html.tw-motion [data-panel].tw-in .tier:nth-child(4){animation-delay:.12s}
html.tw-motion [data-panel].tw-in .tier:nth-child(5){animation-delay:.16s}
/* Nothing above is load-bearing: the panel is already correct before it animates. */
:where(html[data-motion="reduced"],html[data-motion="off"]) [data-panel].tw-in,
:where(html[data-motion="reduced"],html[data-motion="off"]) [data-panel].tw-in .tier{animation:none}

/* ---- pricing product switcher ----------------------------------------------------------
   The pricing table now carries four ladders: the bundle, and each product priced on its own.
   Showing all four at once is a spreadsheet, so they share one slot and a tablist swaps them.

   This is the same [data-tabs] / .itabs / [data-panel] contract tabs.js already serves on
   /context/, so it costs no new JavaScript -- but .itabs itself only existed in app.css, and
   the homepage does not load that. These are the same pills in home.css tokens.

   A tablist and not a filter row, deliberately: a filter narrows one set, a tab swaps one panel
   for another, and this is the second. That is why the buttons carry role="tab" and
   aria-selected rather than aria-pressed, and why arrow keys work -- tabs.js gives that free. */
.itabs{display:flex;flex-wrap:wrap;gap:8px;margin-top:22px}
.itabs button{
  font-family:var(--sans);font-size:calc((13.5px) * var(--ts));color:var(--muted);cursor:pointer;
  border:1px solid var(--rule-strong);background:none;border-radius:999px;padding:7px 15px;
  transition:background .22s,color .22s,border-color .22s;
}
.itabs button[aria-selected="true"]{background:var(--hl);color:var(--on-hl);border-color:transparent}
.itabs button:focus-visible{outline:2px solid var(--mint);outline-offset:2px}
[data-panel][hidden]{display:none}
[data-panel]{margin-top:20px}

/* Context is the one product sold per developer rather than per plan, so its ladder is four
   rungs where the others are five. Its own grid rather than a gap in the five-column one. */
.tiers.four{grid-template-columns:repeat(4,minmax(0,1fr))}

/* The line under a ladder that says what the ladder does not. Governance needs one because
   "unlimited" is only true against credit, and that caveat belongs with the number. */
.pnote{
  margin:18px 0 0;padding-top:15px;border-top:1px solid var(--rule);
  font-size:calc((14px) * var(--ts));line-height:1.6;color:var(--muted);max-width:none;
}
.pnote b{color:var(--ink);font-weight:650}

@media (max-width:900px){ .tiers.four{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:560px){ .tiers.four{grid-template-columns:1fr} }

.compare,.pricing{margin:26px 0 0}
.compare h2,.pricing h2{font-family:var(--sans);font-size:calc((20.5px) * var(--ts));font-weight:600;color:var(--ink);margin:0 0 6px;letter-spacing:-.015em}
.compare .sub,.pricing .sub{color:var(--muted);margin:0 0 22px;font-size:calc((16.5px) * var(--ts))}
.axis{border-top:1px solid var(--rule);padding:20px 0}
.axis:last-of-type{padding-bottom:4px}
.axis-k{margin-bottom:12px}
.axis-row{display:grid;grid-template-columns:1fr 1fr 1.4fr;gap:22px;align-items:center}
.side{display:flex;flex-direction:column;gap:4px}
.side .who{font-family:var(--sans);font-size:calc((14.5px) * var(--ts));color:var(--muted)}
.side .amt{font-family:var(--sans);font-weight:250;font-size:calc((30px) * var(--ts));color:var(--ink);letter-spacing:-.03em}
.side.ours .who{color:var(--mint)}
.side.ours .amt{color:var(--mint);font-weight:400}
.verdict{font-family:var(--sans);font-size:calc((15.5px) * var(--ts));color:var(--body);line-height:1.45}
.verdict b{font-weight:600;color:var(--ink)}

/* Pricing header: the headline and its paragraph on one side, the billing toggle on the other.
   It wraps to two rows below ~820px, where side by side would squeeze the paragraph to a column. */
.pricehead{display:flex;justify-content:space-between;align-items:flex-end;gap:20px 28px;
  flex-wrap:wrap;margin:0 0 26px}
.pricehead-t{flex:1 1 30em;min-width:0}
.pricing .pricehead h2{
  font-size:calc((clamp(22px,2.6vw,29px)) * var(--ts));line-height:1.2;letter-spacing:-.022em;
  margin:0 0 10px;
}
.pricing .pricehead .sub{margin:0;max-width:46em}
.pricehead .terms{margin:0;flex:0 0 auto}

.terms{display:inline-flex;gap:2px;margin:0 0 22px;padding:3px;border-radius:999px;
  border:1px solid var(--rule-strong)}
.terms button{font-family:var(--sans);font-size:calc((14.5px) * var(--ts));color:var(--muted);cursor:pointer;
  border:0;background:none;border-radius:999px;padding:7px 15px;transition:background .22s,color .22s}
.terms button[aria-pressed="true"]{background:var(--hl);color:var(--on-hl)}
.terms .tag{font-size:calc((11.5px) * var(--ts));color:var(--mint);margin-left:6px}
.terms button[aria-pressed="true"] .tag{color:var(--on-hl);opacity:.7}

.pricing[data-term="monthly"] .y-only{display:none}
.pricing[data-term="yearly"]  .m-only{display:none}
.tsave{font-family:var(--sans);font-size:calc((13.5px) * var(--ts));color:var(--mint);margin-top:2px}

/* minmax(0,1fr) rather than 1fr: a bare 1fr track will not shrink below its min-content, so at
   the largest reader text size the tier columns pushed the page wider than the screen instead of
   letting their own text wrap. */
.tiers{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:0;border-top:1px solid var(--rule-strong)}
/* The top padding is the slot the absolutely-positioned .tflag sits in, so it has to scale
   with --ts or a reader on a large type size gets the flag printed over the tier name. Every
   tier reserves it, flagged or not, to keep the names aligned across the row. */
.tier{padding:calc(36px * var(--ts)) 18px 24px;border-right:1px solid var(--rule);display:flex;flex-direction:column;position:relative}
.tier:first-child{padding-left:0}
.tier.featured{padding-left:18px;padding-right:18px}
.tier:last-child{border-right:0;padding-right:0}

.tier.featured{background:var(--tier-hi);box-shadow:inset 0 0 0 1px var(--mint)}
/* Tier badge. A filled pill rather than bare mint text, so "no card required" and "most popular"
   read as labels on the card instead of as another line of copy inside it. Sized to its content,
   which is why `right` is gone: it used to stretch the full card width. */
.tflag{
  position:absolute;top:calc(16px * var(--ts));left:18px;
  font-family:var(--sans);font-size:calc((10.5px) * var(--ts));font-weight:600;
  line-height:1.2;letter-spacing:.11em;text-transform:uppercase;
  background:var(--mint);color:var(--on-mint);
  padding:4px 10px;border-radius:999px;white-space:nowrap;
}
.tier:first-child .tflag{left:0}
/* The featured tier already carries a mint outline, so its badge straddles that line. */
.tier.featured .tflag{top:calc(-9px * var(--ts));left:50%;transform:translateX(-50%)}
.tname{font-family:var(--sans);font-size:calc((15.5px) * var(--ts));font-weight:600;color:var(--ink)}
.tprice{font-family:var(--sans);font-weight:250;font-size:calc((32.5px) * var(--ts));color:var(--ink);letter-spacing:-.03em;margin-top:8px}
.tprice span{font-size:calc((15.5px) * var(--ts));font-weight:400;color:var(--faint);letter-spacing:0}
/* The per-seat line: the ladder's answer to "$149 looks steep". Weighted between the price and
   the note because it IS a price -- the one a reader on a team is actually comparing. */
.tseat{font-family:var(--sans);font-size:calc((13.5px) * var(--ts));color:var(--body);font-weight:500;margin-top:6px}
/* The seat range and whether the rung is an org account at all. Quieter than the per-seat price:
   it qualifies the offer rather than making it. */
.tseats{font-family:var(--sans);font-size:calc((12.5px) * var(--ts));color:var(--faint);margin-top:3px}
/* What happens after the allowance. Same weight as the seat range: it qualifies the offer. */
.tburn{font-family:var(--sans);font-size:calc((12.5px) * var(--ts));color:var(--faint);margin-top:3px}

/* ---- the pay-as-you-go rate card (/pricing/) ----------------------------------------------
   One row per model the API serves. It is a real table because it is tabular data and a reader
   compares down the price column; below 640px the description drops under the model name so the
   row never scrolls sideways. The left rule is the Models colour: a rate is a Models fact. */
.rates{margin:26px 0 0}
.rates .pricehead{margin-bottom:0}
.ratetable{width:100%;border-collapse:collapse;margin-top:20px;border-top:1px solid var(--rule-strong)}
.ratetable th,.ratetable td{font-family:var(--sans);text-align:left;vertical-align:baseline;
  padding:14px 18px 14px 0;border-bottom:1px solid var(--rule)}
.ratetable thead th{font-size:calc((12px) * var(--ts));font-weight:600;color:var(--faint);
  letter-spacing:.06em;text-transform:uppercase;padding-top:10px;padding-bottom:10px}
.ratetable thead th a{color:inherit;text-transform:none;letter-spacing:0;font-weight:500}
.ratetable tbody th a{color:inherit;text-decoration:none}
.ratetable tbody th a:hover{text-decoration:underline;text-underline-offset:3px}
.ratetable tbody th{font-size:calc((15.5px) * var(--ts));font-weight:600;color:var(--ink);
  white-space:nowrap;border-left:2px solid var(--p-models);padding-left:11px}
.ratetable tbody th code{display:block;margin-top:2px;font-family:var(--mono);
  font-size:calc((12px) * var(--ts));font-weight:400;color:var(--faint)}
.ratetable td{font-size:calc((14px) * var(--ts));color:var(--body)}
/* Right padding comes off the LAST column only: with an input and an output column side by side,
   zeroing it on every .num cell ran the two rates together into one string. */
.ratetable .num{text-align:right;white-space:nowrap}
.ratetable .num:last-child{padding-right:0}
.ratetable .rate{font-weight:250;font-size:calc((26px) * var(--ts));color:var(--ink);letter-spacing:-.025em}
/* The leg label is for phones, where the header row is hidden and two bare numbers would not say
   which is input and which is output. On a wide screen the header says it. */
.ratetable .leg{display:none}
/* A model priced by the minute has one price across both rate columns, and says its unit where a token model's header would. */
.ratetable .unit{display:block;margin-top:2px;font-family:var(--sans);font-size:calc((12px) * var(--ts));color:var(--faint)}
@media (max-width:640px){
  .ratetable thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  .ratetable tr{display:grid;grid-template-columns:minmax(0,1fr) auto auto;column-gap:14px;
    border-bottom:1px solid var(--rule);padding:12px 0}
  .ratetable th,.ratetable td{border-bottom:0;padding:0}
  .ratetable tbody th{grid-column:1;grid-row:1;white-space:normal;padding-left:11px}
  .ratetable .num{grid-row:1;display:flex;flex-direction:column;align-items:flex-end}
  .ratetable .leg{display:block;font-family:var(--sans);font-size:calc((11px) * var(--ts));color:var(--faint);
    letter-spacing:.05em;text-transform:uppercase}
  .ratetable td:not(.num){grid-column:1 / -1;grid-row:2;margin-top:6px;padding-left:13px}
  .ratetable .num.span{grid-column:2 / 4}
}
.tnote{font-family:var(--sans);font-size:calc((13px) * var(--ts));color:var(--faint);margin-top:4px;min-height:2.6em}
.tier ul{list-style:none;margin:14px 0 18px;padding:0}
.tier li{font-family:var(--sans);font-size:calc((14px) * var(--ts));color:var(--body);padding:3px 0;border-bottom:1px solid rgba(229,226,218,.7)}
.tier li:last-child{border-bottom:0}
.tier .btn{margin-top:auto;text-align:center;font-size:calc((14.5px) * var(--ts));padding:8px 12px}

.ctas{display:flex;gap:12px;flex-wrap:wrap;justify-content:center;margin:0}
.ctas-lead{margin:44px 0 0;padding-bottom:40px;border-bottom:1px solid var(--rule)}
.btn{
  display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;
  font-family:var(--sans);font-size:calc((15.5px) * var(--ts));border-radius:999px;padding:11px 22px;
  border:1px solid var(--rule-strong);color:var(--ink);background:transparent;
  transition:border-color .2s,background .2s;
}
.btn:hover{text-decoration:none;border-color:var(--ink)}
.btn.p,.btn-p{background:var(--ink);border-color:var(--ink);color:var(--bg)}
.btn.p:hover,.btn-p:hover{opacity:.86}

.navcta .btn{font-size:calc((16px) * var(--ts));font-weight:500;padding:9px 17px}
@media(max-width:980px){
  .navcta .btn{padding:9px 13px;font-size:calc((14.5px) * var(--ts))}
}

/* Font Awesome Free ships its icons under CC BY 4.0, which wants attribution, and the deploy
   build strips every HTML comment and sets esbuild legalComments:none, so a credit in a
   comment would be correct in the source and absent from the served page. It has to be real
   text. Faint, last, and out of the way, but present. */
/* Left, not centred: it used to follow the space-between legal row directly, where a
   centred line read as a footer sign-off. The programme block now sits between them and
   is left-anchored, and a centred line under two left-aligned ones reads as unmoored.
   All three fine-print rows share one edge. */
.foot-credit{margin-top:10px;font-size:calc((12px) * var(--ts));color:var(--faint);text-align:left}
/* display:block, not the flex row it used to be. The footer's children are now three stacked
   bands (the two-column link grid, the legal line, the credit); leaving them in a
   space-between flex row would lay all three side by side. Each band lays itself out. */
.foot{margin:60px 0 0;border-top:1px solid var(--rule);padding:24px 0 56px;
  font-family:var(--sans);font-size:calc((14.5px) * var(--ts));color:var(--faint);
  display:block}

.review-flag{
  font-family:var(--mono);font-size:calc((11.5px) * var(--ts));color:var(--faint);
  border-bottom:1px solid var(--rule);padding:8px 0;text-align:center;
  position:relative;z-index:2;background:var(--bg);
}

@media (max-width:900px){
  .tiers{grid-template-columns:repeat(2,minmax(0,1fr))}
  .tier{border-right:0;border-bottom:1px solid var(--rule);padding:calc(36px * var(--ts)) 0 20px}
  .tnote{min-height:0}

  /* UNPIN THE ROW, or the column count above is a suggestion the grid ignores.
     `.tiers.grouped .tier{grid-row:1 / span 6}` is what stands five tiers side by side sharing
     six subgrid lines, and it is also why they cannot wrap: every tier is explicitly on row 1,
     so the auto-placer cannot move the third one down to a second row. Given two explicit
     columns and five items that all insist on row 1, it creates IMPLICIT columns for the
     overflow instead. Measured at 890px the track list came out
     `41px 41px 223px 227px 220px` -- five tracks where two were asked for, the two real 1fr
     tracks crushed to 41px by whatever the auto tracks left behind. That is the overlap: 41px
     cannot hold "$149", so Explore, Build and Pro printed on top of each other.

     Below this width the ladder is not side by side, so the subgrid it was pinned for is not
     buying anything. Each tier becomes its own flex column -- which is exactly what the
     @supports-not(subgrid) fallback already does, for the same reason. */
  .tiers.grouped{grid-template-rows:none}
  .tiers.grouped .tier{
    display:flex;flex-direction:column;
    grid-row:auto;grid-template-rows:none;
  }
  .tiers.grouped .tier > .btn{align-self:stretch;margin-top:auto}
}
@media (max-width:430px){
  /* Two price columns on a small phone leave roughly 130px each, which is not enough for a
     tier name, a price and a feature list. One per row. */
  .tiers{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:820px){
  /* No header rules here: .navlinks is display:none from 980px down, so anything scoped to it
     at 820px never applied. nav.css owns the header at every width. */

  .axis-row{grid-template-columns:1fr 1fr;gap:14px}
  .verdict{grid-column:1 / -1}
  .lede{font-size:calc((18px) * var(--ts))}
  .glass{padding:22px 18px;border-radius:13px}
  
  .flow{height:min(58vh,430px)}
  .mark{font-size:calc((clamp(43px,16vw,91px)) * var(--ts))}
}

.themeswitch{
  position:relative;display:inline-flex;align-items:center;flex:none;
  padding:3px;border-radius:999px;border:1px solid var(--rule-strong);background:transparent;
}
.themeswitch .thumb{
  position:absolute;top:3px;left:3px;width:32px;height:32px;border-radius:999px;
  background:var(--hl);z-index:0;
  transition:transform .42s cubic-bezier(.22,.61,.36,1), background .3s;
}
:root[data-theme="dusk"]  .themeswitch .thumb{transform:translateX(32px)}
:root[data-theme="night"] .themeswitch .thumb{transform:translateX(64px)}
/* The TYPE and MOTION switches wear the theme switch's pill, but their thumbs follow their own
   position rather than the paper's, hence a data attribute on each group instead of the one on
   <html>.

   The :root prefix is LOAD-BEARING, and the reason is the whole of this bug. The note here used
   to say these were "declared after, so the theme rules above cannot drag it along", and source
   order does not decide this: the theme rules score four class-level components
   (:root, [data-theme], .themeswitch, .thumb) against these three, so they won on specificity
   from anywhere in the file. Every one of these switches was pinned to the theme's own position
   in dusk and night (the text-size thumb sat under "Large" whatever was actually selected).

   And it did not read as a thumb in the wrong place, it read as a control with NO state: the
   selected button paints itself var(--on-hl), the ink meant to sit on the cream thumb, so with
   the thumb elsewhere the one glyph telling you what was selected turned near-black on a
   near-black panel and disappeared.

   :root here adds nothing but a fourth component, which ties the specificity and hands the
   decision back to source order (which is what the old note assumed it already had). */
:root .typeswitch[data-ts-step="0"] .thumb{transform:translateX(0)}
:root .typeswitch[data-ts-step="1"] .thumb{transform:translateX(32px)}
:root .typeswitch[data-ts-step="2"] .thumb{transform:translateX(64px)}
:root .motionswitch[data-mo-step="0"] .thumb{transform:translateX(0)}
:root .motionswitch[data-mo-step="1"] .thumb{transform:translateX(32px)}
:root .motionswitch[data-mo-step="2"] .thumb{transform:translateX(64px)}
.themeswitch button{
  position:relative;z-index:1;width:32px;height:32px;padding:0;border:0;background:none;
  cursor:pointer;color:var(--faint);display:inline-flex;align-items:center;justify-content:center;
  transition:color .3s;
}
.themeswitch button[aria-checked="true"]{color:var(--on-hl)}
.themeswitch button:hover{color:var(--body)}
.themeswitch svg{display:block;overflow:visible}

.ti-core{transition:transform .42s cubic-bezier(.22,.61,.36,1), r .42s ease}
.ti-rays{transform-origin:12px 12px;
  transition:transform .5s cubic-bezier(.22,.61,.36,1), opacity .32s ease}
.ti-shade{transition:transform .46s cubic-bezier(.22,.61,.36,1)}

.themeswitch button[aria-checked="false"] .ti-rays{opacity:.55}

.i-day .ti-rays{opacity:1}
.themeswitch button[aria-checked="true"] .i-day .ti-rays{transform:rotate(0deg) scale(1)}
.i-day .ti-rays{transform:rotate(-35deg) scale(.72)}

.i-dusk .ti-rays{transform:scale(.86)}
.themeswitch button[aria-checked="true"] .i-dusk .ti-rays{transform:scale(1)}

.i-night .ti-shade{transform:translate(2.6px,-2.6px)}
.themeswitch button[aria-checked="true"] .i-night .ti-shade{transform:translate(0,0)}

:where(html[data-motion="reduced"],html[data-motion="off"]) .themeswitch .thumb,:where(html[data-motion="reduced"],html[data-motion="off"]) .ti-core,:where(html[data-motion="reduced"],html[data-motion="off"]) .ti-rays,:where(html[data-motion="reduced"],html[data-motion="off"]) .ti-shade{transition:none}

:root.theme-sweep *,
:root.theme-sweep *::before,
:root.theme-sweep *::after{transition:none !important}

::view-transition-group(root){animation:none}
::view-transition-old(root){animation:none;mix-blend-mode:normal;z-index:1}
::view-transition-new(root){
  mix-blend-mode:normal;z-index:9999;
  animation:theme-sweep 640ms cubic-bezier(.22,.61,.36,1) both;
}
@keyframes theme-sweep{
  from{clip-path:circle(0 at var(--sweep-x) var(--sweep-y))}
  to{clip-path:circle(var(--sweep-r) at var(--sweep-x) var(--sweep-y))}
}

.theme-wave{
  position:fixed;border-radius:50%;z-index:9999;pointer-events:none;
  transform:translate(-50%,-50%) scale(0);will-change:transform;
}
:where(html[data-motion="reduced"],html[data-motion="off"]) .theme-wave{display:none}
/* THE NO-JS NET. Every reduced-motion rule in this file is keyed on data-motion, which the
   inline snippet in each page's <head> writes from the reader's setting, the OS preference and
   this session's cost measurement together (see motion.js). With scripts off, none of that runs
   and the attribute is never written, so the OS preference is honoured directly here, for the
   only case that can reach it.

   Blunt on purpose. It is a reset rather than the hand-tuned end states above because without
   scripts there is nothing to hand-tune AROUND: the choreography that needs an end state is
   gated on html.tw-motion, which only cta-motion.js ever adds, so nothing can be caught
   half-way through a transition here. What is left to stop is decoration that loops. */
@media (prefers-reduced-motion:reduce){
  html:not([data-motion]) *,
  html:not([data-motion]) *::before,
  html:not([data-motion]) *::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
}


.bkoverlay{
  position:fixed;inset:0;z-index:10000;display:none;
  align-items:center;justify-content:center;
  background:rgba(10,12,10,.6);padding:24px;
}
.bkoverlay.open{display:flex}
.bkpanel{
  
  position:relative;width:100%;max-width:760px;height:min(680px,90vh);
  background:#fff;border:1px solid #E5E2DA;border-radius:16px;
  box-shadow:0 24px 64px rgba(0,0,0,.35);overflow:hidden;
}
.bkpanel iframe{width:100%;height:100%;border:0;display:block}
.bkclose{
  position:absolute;top:10px;right:10px;z-index:1;
  width:34px;height:34px;border-radius:50%;
  background:#F4F2EC;border:1px solid #E5E2DA;color:#191917;
  font-size:calc((19.5px) * var(--ts));line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
.bkclose:hover{background:#E5E2DA}
@media(max-width:640px){
  .bkoverlay{padding:0}
  .bkpanel{max-width:none;height:100vh;border-radius:0}
}

/* ---- CTA view (design: "CTA View Redesign") ------------------------------
   Hero rebuilt to the reviewed design: no wordmark (the nav already carries the
   brand, and the h1 is the proposition), a single tappable announcement pill, a
   one-sentence sub-lede, and the fine print on one line.
   The wave field is one full-page-width canvas, muted so it reads as texture rather than
   as a thing competing with the copy, and it is the CTA band: wave-fit.js sizes it to the
   hero's margin box, from the header down to a rule under the hero (.flow-ground). */
.flow{
  /* Muting lives here and nowhere else. It used to be split between this opacity and a
     reduced --wave-alpha, which multiplied out to ~0.27 of the drawn field and was too
     faint to read as motion. No fade at the edges: the field used to feather out across a
     pad top and bottom, and in that pad the still field came back in under it, so the two
     were drawn over each other and the CTA had no edge. It stops at the rule now, as the
     in-hero field on every other page does. The height below is only the fallback for a
     page where wave-fit.js never runs. */
  opacity:.82;
}
/* The ground under that canvas, and the band's edge. The still field (backdrop.css) is fixed behind the whole page, so without this it ran
   under the animated one. wave-fit.js lays this element at the canvas's own top and height: bare paper under the animated field, and the
   rule along its bottom is where the CTA ends and the content (and the still field) starts. --rule-deep, the edge the CTA card itself
   uses, because --rule at 1.24:1 does not read as an edge. It is made by the script that sizes the canvas, so a page where the field does
   not run keeps the still one. It is not the canvas's background because .flow is muted with opacity, which would let a fifth of the
   still field through. content-box, so the rule sits on the row below the canvas rather than under its last row of strands. */
.flow-ground{
  position:absolute;top:0;left:0;width:100%;height:min(150vh,1180px);
  z-index:0;pointer-events:none;background:var(--bg);
  box-sizing:content-box;border-bottom:1px solid var(--rule-deep);
}

/* The CTA is ONE frosted surface, not a scatter of separate plates. Every block in
   here used to carry its own translucent rectangle, so the hero read as four glass
   shards floating over the wave field instead of one card sitting on it. The border
   uses var(--rule) rather than the literal grey .glass hardcodes, so it holds up in
   dusk and night as well as day. */
.ctahero{
  max-width:830px;margin:34px auto;
  padding:clamp(40px,5.5vw,64px) clamp(24px,4vw,48px) clamp(34px,4vw,48px);
  display:flex;flex-direction:column;align-items:center;text-align:center;gap:22px;
  position:relative;z-index:1;
  background:rgba(var(--surface),.34);
  -webkit-backdrop-filter:blur(7px) saturate(1.25);
          backdrop-filter:blur(7px) saturate(1.25);
  /* --rule-deep, not --rule. The hairline that divides rows of text is not an edge for a
     780px frosted card floating over a moving field: at 1.24:1 the card had no boundary and
     read as a smudge in the wave rather than as a surface on top of it.

     This declaration is the FINISHED state and it has to stand entirely on its own. The
     spinning ring in motion.css is painted by a pseudo-element on top of exactly this 1px band,
     and every path that turns the ring off -- reduced motion, motion off, a browser that will
     not register the @property, motion.css failing to load at all -- simply stops painting the
     pseudo and leaves this showing. Nothing here may depend on the animation having run. That
     is the same footgun as the animation-fill-mode:both incident: a base state that is only
     correct once a keyframe has advanced is a section that renders empty when it does not. */
  border:1px solid var(--rule-deep);
  border-radius:22px;
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .ctahero{ background:rgba(var(--surface),.94) }
}
/* The band ends at the bottom of the hero's margin (wave-fit.js draws the rule there), so a card that follows the hero directly
   (/pricing/'s estimate) needs a margin of its own past the rule, or its top edge lands on it. Twice the hero's 34px: the two
   margins collapse, which leaves the rule halfway between the hero and the card. */
.ctahero + .glass{margin-top:68px}
/* ---- the announcement capsule -------------------------------------------------------------
   The only place on the homepage where the newest model release is named, and it links to
   /models/, so it wears the MODELS blue and nothing else.

   Prominence here is bought with FIGURE, not with size, and that is the whole argument. Every
   value this rule used to carry was a de-emphasis value: a var(--rule) edge measuring 1.03:1
   against the composited panel it actually sits on (1.24:1 against bare paper, which is the
   number that token's own comment records), a .72-alpha fill over a card that is itself
   translucent, --muted text at 4.84:1, a mint chip, a mint arrow. --muted PASSES AA and is
   still the problem -- passing AA is what --muted is FOR, it is the token for copy that is
   deliberately subordinate. And mint is the CONTEXT product's colour as well as the link
   colour, so a green chip on a link to the blue product was the one thing on this page
   actively fighting the pillar system built one commit ago.

   Raising the sentence 12.5px -> 15px was tried in the previous commit and did not move it,
   which is the evidence that size was never the failing channel. 15px is unchanged here. This
   capsule sits above a 31.5px h1 and will never win on scale -- and must not: the h1 is the
   page's message, this is the page's news, and both can be first on different channels. What
   it wins on instead is MATERIAL. It is the only opaque, saturated, contoured, lifted object
   in a hero otherwise made entirely of frosted glass and dark text; colour and closure are
   resolved preattentively, before a word has been read, which is what "the first thing a
   reader sees" means mechanically. Final order: capsule (object) -> headline (message) ->
   subtitle -> buttons.

   Nothing below needs the spinning ring around this card, or any animation, to be correct.
   This IS the finished state; motion.css's one-shot arrival ring only sits on top of it. Same
   contract the .ctahero edge one block up was written to, and the same footgun
   (animation-fill-mode:both) it exists to avoid.

   REJECTED: straddling the capsule across .ctahero's top edge as a badge. Genuinely the most
   prominent option on the table and unusable here -- that 1px band is exactly where motion.css
   paints the spinning conic rim, and a chip sitting on a moving multi-colour edge reads as a
   rendering fault, which is the one thing decoration may never do.
   REJECTED: lightening or dropping the .claim plate so the capsule leads by comparison. That
   plate is a measured legibility fix (the band respread put wave strands straight through the
   h1), and trading a legibility fix for a relative-prominence trick is a bad deal. Prominence
   gets added to the capsule, never subtracted from the headline.
   REJECTED: more top padding on .ctahero to give the capsule room above it. That pushes it
   away from the card's leading edge and weakens the one channel -- position -- it was already
   winning. The extra space goes BELOW instead; see .ctahero .ctapill at the foot of this
   block. */

/* Depth for the capsule, and the only shadow in this hero. Declared as a token rather than
   inline because motion.css's arrival ring has to RESTATE it inside every keyframe -- box-shadow
   is one property, so a keyframe naming only the ring would replace the resting shadow for the
   animation's duration and the capsule would drop flat for 1.15s and then land again.
   Two layers on purpose: a 1px contact shadow that draws the bottom edge, so the capsule sits
   ON the card rather than floating over it, and a wider soft one for the lift. Per theme,
   because a shadow tuned for #FBFAF8 paper is invisible on #0D141D and one tuned for night is
   a smear on day; night uses a deeper, tighter pair, since dark paper swallows spread and the
   contact edge has to do the work there.
   These three rules sit here rather than in the theme blocks at the top of the file so the
   reasoning stays next to the component that consumes it. The [data-theme] selectors outrank
   the bare :root regardless of order, so nothing depends on their position. */
:root                    { --pill-lift:0 1px 1px rgba(30,26,18,.07), 0 3px 10px rgba(30,26,18,.09) }
:root[data-theme="dusk"] { --pill-lift:0 1px 1px rgba(30,26,18,.10), 0 3px 10px rgba(30,26,18,.12) }
:root[data-theme="night"]{ --pill-lift:0 1px 1px rgba(0,0,0,.45),    0 4px 14px rgba(0,0,0,.38) }
/* The RAISED shadow has to be a per-theme token for the same reason the resting one is, and
   shipping it as day-tuned literals made hover go backwards on night: rgba(30,26,18,.10) over
   #0D141D measures 1.008:1 against the panel, where the RESTING --pill-lift is rgba(0,0,0,.45)
   at 1.066:1. The pill therefore lost elevation at the moment it was picked up -- the one frame
   where it must gain some. Each step is stronger than its own theme's rest, not stronger than
   day's. */
:root                    { --pill-lift-hi:0 2px 3px rgba(30,26,18,.10), 0 8px 20px rgba(30,26,18,.14) }
:root[data-theme="dusk"] { --pill-lift-hi:0 2px 3px rgba(30,26,18,.14), 0 8px 20px rgba(30,26,18,.19) }
:root[data-theme="night"]{ --pill-lift-hi:0 2px 4px rgba(0,0,0,.58),    0 10px 26px rgba(0,0,0,.50) }

.ctapill{
  display:inline-flex;align-items:center;gap:10px;text-decoration:none;
  /* OPAQUE, and this is a correctness fix as much as a design one. rgba(var(--surface),.72)
     sat over a frosted panel sat over a live wave canvas, which means the pill's actual
     contrast was a function of where the wave strands happened to be -- there was no ratio to
     state, only a range. Every number in this block is now fixed. It was also the same
     MATERIAL as its own container, which is why the eye read straight past it to the headline
     plate; a solid fill is what turns a region into an object.
     backdrop-filter went with it: it did nothing behind an opaque fill and cost a compositing
     layer on a card already paying for a conic gradient, a mask and a canvas -- and motion.js
     can demote the whole page to 'reduced' if per-frame cost trips, which would cost the reader
     the shimmer, the door handoff and the wave field, not just this.
     REJECTED: a full --p-models fill with reversed text at REST. The .txt is a 76-character
     sentence, and that much reversed 15px body copy is a real reading cost; the near-black
     .btn-p directly below already owns the "solid slab" slot in this hero. The saturated blue
     goes on the chip and the border, which are small and are fills, so they can carry it at
     full strength. It is what :hover escalates to. */
  background:var(--p-models-soft);
  /* 1px, not 2. The edge has to clear SC 1.4.11's 3:1 -- the same floor the .ctahero card edge
     was just fixed to -- and at full-strength --p-models it does that at width 1: 3.87/3.79/8.14
     inward against its own surface, 5.12/4.95/10.63 outward against the panel. Widening it
     would only add weight to an edge that already passes, and this capsule sits INSIDE the
     spinning four-colour ring: a heavy rim here would read as a second, competing ring rather
     than as a plate. One flat hue and a 999px radius, so it does not. */
  border:1px solid var(--p-models);border-radius:999px;
  /* 7 -> 8 vertical. An opaque plate needs a little more air inside it than a translucent one
     before it reads as a pressed object rather than as a highlight run over the text. */
  padding:8px 16px 8px 9px;
  box-shadow:var(--pill-lift);
  transition:background-color .2s,border-color .2s,box-shadow .2s,transform .2s;
}
/* Solid fill, reversed text -- the one saturated shape in the hero, and the thing the eye
   lands on first. It replaces mint-on-mint-soft (4.65/4.80/7.42), which was both the wrong
   product's colour and a tint where this wants a fill. Two effects, both wanted: it puts the
   most saturated pixels on the page at the LEADING edge of the capsule, where a left-to-right
   reader enters it, and a solid chip inside a tinted plate inside a frosted card is three
   legible depths. */
.ctapill .tag{
  font-family:var(--mono);font-size:calc((11px) * var(--ts));text-transform:uppercase;
  letter-spacing:.08em;color:var(--on-p-models);background:var(--p-models);
  border-radius:999px;padding:3px 8px;flex:none;
  transition:background-color .2s,color .2s;
}
/* --ink, not --muted. On the old surface --muted measured 4.84/5.36/5.89: it passed AA and
   still read as secondary, which is what that token is for. On the new plate --ink measures
   10.58/8.38/12.11. Weight 500 rather than 400 adds optical mass without adding a single pixel
   of size, which is the whole point -- the size relationship to the 31.5px h1 is left exactly
   as it was.
   15px is unchanged from the previous commit, which raised it from 12.5px because at 12.5 it
   read as a caption under the nav, smaller than the .ctafine legal line below the buttons.
   Still below .ctasub, so the headline keeps type primacy. */
.ctapill .txt{font-size:calc((15px) * var(--ts));font-weight:500;color:var(--ink);
  text-align:left;transition:color .2s}
/* The arrow takes --ink too, so it terminates the sentence rather than floating free of it.
   REJECTED: --p-models, which is where it wants to go. On the new plate it measures
   3.87/3.79/8.14 -- fine against SC 1.4.11's 3:1, since the glyph is aria-hidden decoration,
   but two full stops fainter than the sentence it ends, and a 14px glyph that much fainter
   than its own line reads as a disabled control. If a blue arrow is ever wanted, the honest
   form is a solid disc -- --p-models fill, --on-p-models glyph, 6.44/7.96/10.63, the same pair
   as the chip -- which is a bigger change to the silhouette than this needs. */
.ctapill .arw{font-size:calc((14px) * var(--ts));color:var(--ink);flex:none;
  transition:color .2s,transform .2s}

/* Hover INVERTS the capsule instead of tinting its edge. The old rule moved border-color to
   var(--mint) -- the wrong product's colour, on a border nobody could see at rest anyway, so
   the hover had nothing to escalate FROM.
   Chip and plate swap roles, which needs no third token and lands every pair on the same
   measured ratio: 6.44/7.96/10.63 for the text on the lozenge AND for the chip text on the
   inverted chip. border-color rides along so the lozenge is one flat shape, and the lift
   deepens rather than changing character.
   The arrow nudge is HOVER-ONLY on purpose. A perpetual nudge was rejected outright (see the
   note in motion.css); as a hover response it answers a reader who has already decided to
   look, rather than soliciting one who has not. Nobody has ever called a hover state busy. */
.ctapill:hover,.ctapill:focus-visible{
  background:var(--p-models);border-color:var(--p-models);transform:translateY(-1px);
  box-shadow:var(--pill-lift-hi);
}
/* Focus keeps the RESTING elevation, not the raised one, and that is deliberate. The 2px --mint
   ring sits at outline-offset:3px, which is exactly the band the raised shadow's 8-20px spread
   paints into; measured against the day panel the ring falls 4.20 -> 3.18 normal and 3.80 ->
   2.88 under protanopia, i.e. through SC 1.4.11's 3:1 floor. The affordance for a keyboard user
   is the ring, not the lift, so the lighter ground is the one worth keeping. A pointer user
   hovering still gets the full raised shadow, because a pointer user has no ring to protect. */
.ctapill:focus-visible{ box-shadow:var(--pill-lift) }
.ctapill:hover .txt,.ctapill:focus-visible .txt,
.ctapill:hover .arw,.ctapill:focus-visible .arw{color:var(--on-p-models)}
.ctapill:hover .tag,.ctapill:focus-visible .tag{
  background:var(--on-p-models);color:var(--p-models);
}
.ctapill:hover .arw{transform:translateX(3px)}
/* This is the first interactive element in <main> and it had no focus ring at all. The
   inversion above is not a focus indicator on its own -- a keyboard reader gets no hover state
   to compare it against.
   --mint at 2px, because that is what every other focus ring in this stylesheet uses
   (.menubtn, .appearance-btn, .ap-reset, .itabs button). A focus ring is a system affordance,
   not brand decoration, and one consistent colour for "you are here" is worth more than
   colour-matching each component to itself.
   REJECTED: --ink, which measures better against the panel (13.99/10.96/15.82 vs mint's
   4.20/3.90/9.75) and breaks the only focus convention this file has. Both clear SC 1.4.11's
   3:1 in all three themes, so the extra headroom buys nothing a reader can use.
   The OFFSET is load-bearing, and is 3px here rather than the 2px used elsewhere, because
   unlike those buttons this element inverts to a saturated blue on focus: the offset leaves a
   band of PANEL on both sides of the ring, so the ring's adjacent colour is the panel and
   never the lozenge it encircles. 3px rather than 2 because a straight-edged outline crowds a
   999px radius at 2. */
.ctapill:focus-visible{outline:2px solid var(--mint);outline-offset:3px}

/* ISOLATION. .ctahero is a flex column with a uniform 22px gap, so the capsule was row one of
   five equally spaced rows -- the spacing said "list item", not "announcement". 10px more
   below it (32px effective) separates it from the stack it introduces. Taken from the hero's
   own vertical slack; nothing else moves and the h1 gives up nothing. Below only -- see the
   rejected top-padding note above. */
.ctahero .ctapill{margin-bottom:10px}

/* ---- k-context: the capsule pointed at something other than the model release -----------
   Same component, a different accent and a different silhouette, so a reader who has seen the
   model pill before notices this is not that pill. Shape carries as much of that as colour:
   14px is the radius this component ALREADY uses below 620px (see the media query above), so a
   returning visitor on desktop now sees the same "chip, not capsule" silhouette mobile has
   always had, rather than a shape invented for this alone. Everything else -- the opaque plate,
   the reversed-chip pattern, the hover inversion, the focus ring -- is the base .ctapill
   design; only the four colour roles and the corner radius are swapped. */
.ctapill.k-context{
  background:var(--p-context-soft);border-color:var(--p-context);border-radius:14px;
}
.ctapill.k-context .tag{color:var(--on-p-context);background:var(--p-context)}
.ctapill.k-context:hover,.ctapill.k-context:focus-visible{
  background:var(--p-context);border-color:var(--p-context);
}
.ctapill.k-context:hover .txt,.ctapill.k-context:focus-visible .txt,
.ctapill.k-context:hover .arw,.ctapill.k-context:focus-visible .arw{color:var(--on-p-context)}
.ctapill.k-context:hover .tag,.ctapill.k-context:focus-visible .tag{
  background:var(--on-p-context);color:var(--p-context);
}

/* NARROW: the capsule stops being a capsule. The .txt is a 76-character sentence, so under
   roughly 640px it wraps to three or four lines and the 999px radius resolves to half the
   BOX height -- a ~90px tall stadium with two large empty lobes either side of the chip and
   the arrow. That geometry was always here; it was invisible while the pill was a .72-alpha
   tint of its own container, and turning it into an opaque saturated plate is exactly what
   made it conspicuous. Fixing it belongs with the fill that exposed it.
   14px, so a wrapped announcement reads as a notice card -- which is what it is at that size.
   Alignment stays centred: the chip and the arrow bracket the block, and flex-start would
   strand the arrow in the top-right corner away from the line it terminates.
   REJECTED: shortening the sentence to fit one line on a phone. The model name and the two
   numbers ARE the announcement; there is nothing in that string to cut that does not cost the
   reason a reader would tap it.
   REJECTED: hiding the arrow under 640px to buy width. It is the only affordance saying the
   capsule is a link, and the whole point of this component is that it gets tapped. */
@media(max-width:640px){
  .ctapill{border-radius:14px;padding:10px 14px 10px 10px}
}

/* Plate behind the headline. The band respread put wave strands straight through the h1,
   and unlike the smaller copy it had nothing behind it. 0.6 alpha is deliberately lighter
   than the 0.72 used elsewhere: the headline is near-black and heavy, so it only needs the
   field knocked back, not hidden. In a centred flex column the block hugs its longest line,
   so the plate tracks the text rather than spanning the hero. */
.ctahero .claim{margin:0;font-size:calc((clamp(30px,5.4vw,58px)) * var(--ts));
  line-height:1.06;font-weight:700;letter-spacing:-.03em;text-wrap:balance;color:var(--ink);
  padding:14px 28px;border-radius:22px;
  background:rgba(var(--surface),.6);
  -webkit-backdrop-filter:blur(8px) saturate(1.1);
          backdrop-filter:blur(8px) saturate(1.1);}
/* The support line is intentionally substantial enough to read without competing with the
   headline. Its measure follows the wider hero while remaining comfortable to scan. */
.ctasub{margin:0;font-size:calc((20px) * var(--ts));line-height:1.5;color:var(--muted);
  max-width:710px;text-wrap:pretty;}
.ctahero .ctas{display:flex;align-items:center;gap:14px;margin-top:10px;flex-wrap:wrap;
  justify-content:center}
.ctafine{font-size:calc((15px) * var(--ts));font-weight:500;line-height:1.5;color:var(--muted)}
/* ---- certification marks in the hero ---------------------------------------------------------
   Two supplied shields hang from the card's top corners. scripts/site-chrome.mjs stamps them as
   the card's last child, so they read after the offer and the buttons; they are placed here, not
   by their position in the markup.

   The wrapper is out of flow (absolute, inset:0) so the flex column's 22px gap is not spent on
   it, and it is click-through so it never sits over the card; only the two shields take the
   pointer. They start 12px inside the top edge, never on it: motion.css paints its one-shot ring
   on that 1px band, and a mark straddling a moving edge reads as a rendering fault -- the same
   reason the announcement capsule above was refused this position.

   The headline's second line runs nearly the full width of the card, so the shields have to end
   above it. At their widest (78px) they are 101px tall and finish inside the first line's band,
   and that line is short, so there is horizontal clearance as well. Under 760px the card narrows
   until line one reaches the corners; there the marks drop into the card's flow, under the fine
   print, and the status is said in words. */
.ctacerts{position:absolute;inset:0;pointer-events:none}
.ctacerts > a.cert-m{
  position:absolute;top:12px;display:block;line-height:0;pointer-events:auto;
  border-radius:6px;background-image:none;
}
.ctacerts > a.cert-m:first-of-type{left:clamp(12px,2vw,22px)}
.ctacerts > a.cert-m:nth-of-type(2){right:clamp(12px,2vw,22px)}
.ctacerts > a.cert-m:focus-visible{outline:2px solid var(--mint);outline-offset:3px}
/* The width also gives way to the reader's text-size control: the headline grows with --ts and
   at the 1.5 maximum its first line reaches the corner, so the shield shrinks by the same
   factor (78px at 1, 52px at 1.5) rather than sit on the letters. Measured, not assumed:
   without this, 1.5 at 1100px and above overlapped the first line by up to 16px. */
.ctacerts .cert-img{
  display:block;height:auto;
  width:clamp(48px,min(max(60px,6vw),calc(78px / var(--ts))),78px);
}
.ctacerts-t{display:none}
@media (max-width:760px){
  .ctacerts{
    position:static;display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
    gap:8px 14px;pointer-events:auto;
  }
  .ctacerts > a.cert-m{position:static}
  .ctacerts .cert-img{width:62px}
  .ctacerts-t{
    display:block;flex:1 1 100%;margin:0;text-align:center;
    font-size:calc((13px) * var(--ts));line-height:1.45;color:var(--muted);
  }
}
/* Two jobs, each with its proof folded into the same coloured surface. This keeps the evidence
   beside the promise it supports instead of creating a second competing metrics row.
   No accent bar down the left edge (removed 2026-09-30): the tint, the hue-coloured border and the
   hue-coloured label already say which product a card is about, and the bar was a fourth signal
   for the same fact. Padding is symmetric now that nothing sits in the left gutter. */
.ctaoffers{width:100%;list-style:none;margin:-2px 0 0;padding:0;display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
.ctaoffers li{min-width:0}
.ctaoffers a{display:flex;min-height:100%;flex-direction:column;
  align-items:flex-start;gap:5px;padding:15px 18px;
  text-align:left;text-decoration:none;color:var(--ink);line-height:1.35;
  border:1px solid color-mix(in srgb,var(--hue) 34%,var(--rule));border-radius:14px;
  background:color-mix(in srgb,var(--hue) 9%,rgb(var(--surface)))}
.ctaoffers b{font-size:calc((17px) * var(--ts));font-weight:700;color:var(--hue)}
.ctaoffers span{font-size:calc((15.5px) * var(--ts));color:var(--ink)}
.ctaoffers strong{font-weight:700;color:var(--hue)}
.ctaoffers a:hover span{text-decoration:underline;text-underline-offset:3px}
.ctaoffers .k-models{--hue:var(--p-models)} .ctaoffers .k-context{--hue:var(--p-context)} .ctaoffers .k-gov{--hue:var(--p-gov)}
@media (max-width:640px){.ctaoffers{grid-template-columns:1fr;gap:10px}}
/* The third item in the hero's button row is a link, not a button: the row already spends its
   weight on the conversion CTA and its secondary, and a third pill of either silhouette read as
   a duplicate control. It sits on the buttons' centre line and gets the same tap height. */
.ctalink{display:inline-flex;align-items:center;gap:7px;min-height:44px;padding:0 6px;
  font-size:calc((15.5px) * var(--ts));font-weight:500;color:var(--ink);text-decoration:none;
  border-bottom:1px solid transparent}
.ctalink svg{transition:transform .18s ease}
.ctalink:hover{color:var(--mint)}
.ctalink:hover svg{transform:translateX(2px)}

/* Air between the rail and the first card under it. The rail's own bottom padding is 12px, tuned for
   the sub-pages, where the next section brings its own top padding; the homepage's card brings none,
   so the space is added here, on the homepage only. */
.ctahero + .mqs{padding-bottom:46px}
/* The capsule's children are added to this kill because they now carry transitions of their
   own. Without them a reduced-motion reader would get the plate easing to blue underneath text
   that had already flipped to white -- roughly 200ms of white-on-pale-blue, which is the one
   moment in this design that measures badly.
   Transitions, not animations, so there is no fill-mode hazard here: every colour is declared
   in the resting rules above, and a transition that never runs leaves the capsule already
   correct. The no-JS net further up this file already reaches these, since it selects
   html:not([data-motion]) * and clamps transition-duration. */
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctapill,
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctapill .tag,
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctapill .txt,
:where(html[data-motion="reduced"],html[data-motion="off"]) .ctapill .arw{transition:none}

/* ---- site footer -----------------------------------------------------------------------
   The same markup is used site-wide. On wide screens the product and resource lists flank the
   mark; on phones the mark becomes a compact header and the lists stay side by side. Keeping
   those lists in two columns avoids the long, mostly empty vertical footer the old stacked
   mobile layout created. */
.foot{border-top:0}
.footgrid{
  display:grid;
  grid-template-columns:minmax(0,1fr) clamp(150px,18vw,210px) minmax(0,1fr);
  grid-template-areas:"product mark more";
  align-items:stretch;gap:clamp(28px,5vw,72px);
  padding:clamp(28px,4vw,48px);
  border:1px solid var(--rule);border-radius:22px;
  background:color-mix(in srgb,var(--mint) 3%,var(--bg));
}
.footcol{display:flex;flex-direction:column;gap:0;min-width:0}
.footcol:first-child{grid-area:product}
.footcol:last-child{grid-area:more}
.footcol-h{
  margin:0 0 12px;
  font-family:var(--sans);font-size:calc((11px) * var(--ts));font-weight:700;
  letter-spacing:.15em;text-transform:uppercase;color:var(--muted);
}
.footcol > a{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  min-height:42px;padding:8px 0;border-bottom:1px solid var(--rule);border-radius:0;
  font-size:calc((15px) * var(--ts));color:inherit;background-image:none;
  transition:color .18s ease,padding-left .18s ease;
}
.footcol > a::after{
  content:"\2192";flex:none;color:var(--mint);opacity:0;transform:translateX(-5px);
  transition:opacity .18s ease,transform .18s ease;
}
.footcol > a:hover{color:var(--mint);padding-left:4px;text-decoration:none;background-size:0 0}
.footcol > a:hover::after,.footcol > a[aria-current="page"]::after{
  opacity:1;transform:translateX(0)
}
.footcol > a[aria-current="page"]{color:var(--mint);font-weight:650}
.footcol > a:focus-visible{outline:2px solid var(--mint);outline-offset:4px;border-radius:2px}
.footmid{
  grid-area:mark;display:grid;place-items:center;min-height:100%;padding:24px;
  border:1px solid var(--rule);border-radius:18px;background:var(--bg);opacity:1;
}
.footmid svg{display:block;width:90px;max-width:100%;height:auto;opacity:.78}
.footlegal{
  display:flex;gap:10px 28px;flex-wrap:wrap;justify-content:space-between;text-align:left;
  margin-top:20px;padding-top:18px;border-top:1px solid var(--rule);
}
.footlegal a,.foot-credit a{padding:0;border-radius:2px;font-size:inherit}
/* The company line is the last row of the footer's own text: the links come first at every width and it sits under them on a
   line of its own. The flex basis takes the whole row, so it wraps even where it would fit beside the links; on a phone the row
   is a grid and this does nothing. */
.footlegal > :last-child{flex:0 0 100%}

/* ---- startup-programme row -------------------------------------------------------------
   Sits between the legal row and the glyph credit, in both stylesheets, because the footer
   markup is shared and these two files are separate copies of its layout.

   Modelled on the badge-and-name rows other startups use: a programme that issues a badge is
   shown as its badge, one that does not is shown as its name, and the two sit on one baseline.
   That mix is why the marks are plain text rather than the pills this row started as -- a pill
   beside a supplied badge image reads as two different kinds of thing. Heights are normalised
   on the image, never the box, so a badge with its own frame keeps its own proportions.

   `.footprog > a` -- see the note on .footcol > a: home.css paints every footer link through
   `.foot a` at (0,1,1), which outranks a bare class, and the night theme is where that shows.
   The marks live one level deeper here, so the selector carries .footprog-row. */
/* The bottom margin is the gap the removed attribution paragraph used to provide; without
   it the glyph credit sits 10px under the marks and reads as part of this block. */
.footprog{margin:18px 0 16px}
.footprog-k,.footcert-k{
  margin:0 0 10px;
  font-family:var(--sans);font-size:calc((11px) * var(--ts));font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--faint);
}
.footprog-row{display:flex;flex-wrap:wrap;align-items:center;gap:10px clamp(20px,3vw,34px)}
.footprog-row > a{
  display:inline-flex;align-items:center;padding:0;border-radius:3px;
  font-size:calc((13px) * var(--ts));font-weight:650;color:var(--muted);
  background-image:none;transition:color .18s ease;
}
.footprog-row > a:hover{color:var(--mint);text-decoration:none;background-size:0 0}
.footprog-row > a:focus-visible{outline:2px solid var(--mint);outline-offset:4px}
/* The badge is the supplied artwork and is never recoloured, tinted, cropped or given a border
   of ours -- whatever frame is in the file is part of the mark. What IS ours is the plate under
   it. Both files we were given are light-ground (NVIDIA's inside its own black frame, AWS's on
   bare white), so without a plate the night theme gets two white rectangles bleeding onto near
   black paper with hard edges. The plate makes that a deliberate card instead, and on the two
   light papers it is nearly invisible against them. No border on the plate: NVIDIA's badge
   already carries one and a second would read as a double frame. */
.footprog-row > a.has-badge{
  padding:6px 8px;border-radius:7px;background:#fff;
  transition:box-shadow .18s ease,transform .18s ease;
}
.footprog-row > a.has-badge:hover{box-shadow:0 1px 8px rgb(0 0 0 / .16);transform:translateY(-1px)}
.prog-img{display:block;width:auto;height:36px;max-width:210px;object-fit:contain}
.prog-img-dark{display:none}

/* The badge pair, if a programme supplies one. A single-image badge stays as it is in all three
   themes, which is correct for artwork that carries its own white frame. */
:root[data-theme="night"] .prog-img-light{display:none}
:root[data-theme="night"] .prog-img-dark{display:block}

/* ---- certification row -----------------------------------------------------------------
   Above the programme row, in both stylesheets for the same reason. The two marks are shields
   drawn on their own white ground, so unlike the programme badges they take no plate from us;
   on night's paper they read as two white cards, which is what a badge is. Sized by height so
   the pair shares a line whatever the file's ratio, and tall enough that the shield's own title
   (SOC 2, ISO 27001) can be read. Its "in progress" label cannot, at this size, so the status is
   also said in words beside them. The anchors carry .footcert-row like .footprog-row does:
   `.foot a` above sits at (0,1,1) and paints a highlighter behind every footer link. */
.footcert{margin:18px 0 0}
.footcert-row{display:flex;flex-wrap:wrap;align-items:center;gap:12px clamp(16px,2.4vw,26px)}
.footcert-row > a.cert-m{
  display:block;padding:0;border-radius:6px;line-height:0;background-image:none;
  transition:transform .18s ease;
}
.footcert-row > a.cert-m:hover{transform:translateY(-2px)}
.footcert-row > a.cert-m:focus-visible{outline:2px solid var(--mint);outline-offset:4px}
.footcert .cert-img{display:block;width:auto;height:88px}
.footcert-t{margin:0;max-width:30ch;font-size:calc((13px) * var(--ts));line-height:1.5;color:var(--muted)}
.footcert-t a{padding:0;border-radius:2px;font-size:inherit}

/* ---- programmes left, security right, one line ------------------------------------------------
   The programme row and the certification row used to stack, which put a second full row of height
   into every footer. They now share the line: the programmes where they always were, on the left,
   and the security marks on the right. .foottrust is the flex container (scripts/site-chrome.mjs
   stamps it) and margin-left:auto keeps the security block against the right edge even when the
   line wraps. Top-aligned, so the two kickers share a line and the taller shields hang below it.
   Under 820px there is no room for both, so they stack, and security goes first, above the
   sponsors. */
.foottrust{
  display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;
  gap:18px 40px;margin:18px 0 16px;
}
.foottrust > .footprog,.foottrust > .footcert{margin:0}
.foottrust > .footcert{margin-left:auto}
@media (max-width:820px){
  .foottrust{flex-direction:column;align-items:flex-start;gap:16px;margin:16px 0 14px}
  .foottrust > .footcert{order:-1;margin-left:0}
}


@media (max-width:700px){
  .footgrid{
    grid-template-columns:repeat(2,minmax(0,1fr));
    grid-template-areas:"mark mark" "product more";
    gap:24px 28px;padding:20px;border-radius:18px;
  }
  .footmid{place-items:center start;min-height:80px;padding:16px 18px}
  .footmid svg{width:66px}
  .footcol-h{margin-bottom:8px}
  .footcol > a{min-height:40px;font-size:calc((14px) * var(--ts))}
  .footlegal{display:grid;gap:8px;justify-content:start;text-align:left;margin-top:18px}
  .footprog{margin:16px 0 14px}
  .footprog-row{gap:10px 22px}
  .footcert .cert-img{height:76px}
  .footcert-t{max-width:none;flex:1 1 100%}
  .foot-credit{text-align:left}
}

/* ---- The platform scene -----------------------------------------------------------------
   Slide 3 of the pitch deck as a live diagram: the same agents -> Tileward -> model path, with
   a request you can watch travel it. Documents sits under Governance and Context as the store
   recall draws out of and the answer cites back to; the dashed envelope is the deployment
   boundary. Two topologies on tabs (tabs.js, no new script), one inline SVG each.

   AT REST THE SCENE IS COMPLETE. Every node, wire and label is drawn and a single request sits
   parked at its agent; the animation only moves packets and lights boxes as they arrive, and
   every animation rule is behind the motion gate, so a reader with motion reduced or off, or
   with JavaScript off, gets the finished diagram rather than a blank one. */
.scenesec{padding:40px 28px 40px}
/* Phone: the column scene needs the width. .wrap keeps 28px either side and this section another 28,
   which left the 420-unit drawing 261px wide and its 13px labels at 8px. Out to the wrap's edge. */
@media (max-width:700px){.scenesec{margin-left:-20px;margin-right:-20px;padding:32px 8px 28px}}
.tw-scene.m{display:none} @media (max-width:700px){.tw-scene.d{display:none} .tw-scene.m{display:block}}
.tw-scene.m .ct{font-size:12.5px} .tw-scene.m .nt{font-size:13.5px} .tw-scene.m .ns{font-size:13px} .tw-scene.m .hd{font-size:13px} .tw-scene.m .hs{font-size:12.5px} .tw-scene.m .lab{font-size:12.5px} .tw-scene.m .tag{font-size:13px} .tw-scene.m .rec{font-size:12.5px} .tw-scene.m .pt{font-size:10.5px}

.tw-scene{width:100%;height:auto;display:block;--sc-line:var(--rule-strong,#cfcbc2)}
.tw-scene text{font-family:var(--sans,-apple-system,Inter,sans-serif);fill:var(--body)}
.tw-scene .hd{font-size:12.5px;font-weight:700;letter-spacing:.13em;text-transform:uppercase;fill:var(--muted)}
.tw-scene .hs{font-size:11.5px;fill:var(--muted)}
.tw-scene .lab{font-size:11px;fill:var(--muted);letter-spacing:.03em}
.tw-scene .host rect{fill:rgba(var(--surface,255,255,255),.45);stroke:var(--rule);stroke-width:1}
.tw-scene .env rect{fill:color-mix(in srgb,var(--mint,#0E7A62) 3%,transparent);stroke:color-mix(in srgb,var(--mint,#0E7A62) 45%,var(--rule));stroke-width:1;stroke-dasharray:5 5}
.tw-scene .env .hd{fill:var(--mint,#0E7A62)}
.tw-scene .nd rect,.tw-scene .recbox rect{fill:var(--bg);stroke:var(--rule);stroke-width:1.25}
/* Each stage wears its product colour at rest -- a tinted fill, a tinted rule, the title in the colour --
   so the scene reads in light mode without waiting for a stage to light up. The lit state deepens it. */
.tw-scene .gov rect{fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg));stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule))}
.tw-scene .ctx rect{fill:color-mix(in srgb,var(--p-context) 6%,var(--bg));stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule))}
.tw-scene .doc rect{fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg));stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule))}
.tw-scene .mdl rect{fill:color-mix(in srgb,var(--p-models) 6%,var(--bg));stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule))}
.tw-scene .gov .nt{fill:var(--p-gov)} .tw-scene .ctx .nt{fill:var(--p-context)} .tw-scene .doc .nt{fill:var(--p-docs)} .tw-scene .mdl .nt{fill:var(--p-models)}
.tw-scene .nt{font-size:12.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}
.tw-scene .ns{font-size:12px;fill:var(--muted)}
.tw-scene .wire{fill:none;stroke:var(--sc-line);stroke-width:1.25}
.tw-scene .wire.link{stroke-dasharray:3 4}
.tw-scene .thr rect{fill:var(--rule)} .tw-scene .thr.faint rect{opacity:.7}
.tw-scene .gst rect{fill:var(--muted);opacity:.55}
.tw-scene .mv{transform-box:view-box;transform-origin:0 0}
.tw-scene .ghost{transform-box:view-box;transform-origin:0 0}
.tw-scene .cd{fill:var(--bg);stroke:var(--rule);stroke-width:1.25}
.tw-scene .ct{font-size:11.5px;fill:var(--ink)}
.tw-scene .a1 .cd{stroke:var(--p-models)}
.tw-scene .chip rect{fill:color-mix(in srgb,var(--p-docs) 12%,var(--bg));stroke:var(--p-docs);stroke-width:1}
.tw-scene .chip text{font-size:9.5px;fill:var(--p-docs);font-weight:700}
.tw-scene .pb{fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg));stroke:var(--p-docs);stroke-width:1}
.tw-scene .pt{font-size:9px;fill:var(--p-docs);font-weight:700}
.tw-scene .tag{font-size:11.5px;font-weight:600;opacity:0}
.tw-scene .tag.ok{fill:var(--p-context)} .tw-scene .tag.no{fill:var(--p-gov)} .tw-scene .tag.turns{fill:var(--p-context);font-weight:500}
.tw-scene .rec{font-size:10.5px;fill:var(--muted);opacity:0}
.tw-scene .rec .ok{fill:var(--p-context);font-weight:700} .tw-scene .rec .no{fill:var(--p-gov);font-weight:700}
/* at rest: the question sits with the agent above the whole thread; nothing else has happened yet */
.tw-scene .q1{opacity:1} .tw-scene .ghost,.tw-scene .psg,.tw-scene .a1,.tw-scene .q2{opacity:0}

/* The meter, SVG text (no foreignObject: WebKit mis-painted opacity-animated HTML inside one). At
   rest it shows the allowed turn's final numbers, the comparison a still reader should get;
   scene.js overwrites them in step with the cards and reveals the running block, only true live. */
.tw-scene .meter rect{fill:var(--bg);stroke:var(--rule);stroke-width:1}
.tw-scene .m-h{font-size:12.5px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;fill:var(--muted)}
.tw-scene .m-k,.tw-scene .m-ar{font-size:13px;fill:var(--muted)}
.tw-scene .m-v{font-size:13px;font-weight:650;fill:var(--ink);font-variant-numeric:tabular-nums}
.tw-scene .m-v.sent,.tw-scene .m-v.tsent{fill:var(--p-context)} .tw-scene .m-v.out,.tw-scene .m-v.tout{fill:var(--p-models)}
.tw-scene .m-pct{font-size:11.5px;font-weight:700;fill:var(--p-context)}
.tw-scene .m-note{font-size:11px;font-weight:600;fill:var(--p-gov);opacity:0}
.tw-scene .m-run{display:none} html.tw-count .tw-scene .m-run{display:inline}
.tw-scene.m .m-h{font-size:13px} .tw-scene.m .m-k,.tw-scene.m .m-ar,.tw-scene.m .m-v{font-size:14.5px} .tw-scene.m .m-pct{font-size:13px} .tw-scene.m .m-note{font-size:12.5px}
/* the count on each wire: what left the agent, what the gate let through, what the model was charged for, what came back */
.tw-scene .wl{font-size:11px;fill:var(--muted);letter-spacing:.02em}
.tw-scene .wn{font-weight:700;fill:var(--ink);font-variant-numeric:tabular-nums;letter-spacing:0}
.tw-scene .ww{font-size:10.5px}
.tw-scene [data-k="thru"]{fill:var(--p-gov)} .tw-scene [data-k="chg"]{fill:var(--p-context)} .tw-scene [data-k="out"]{fill:var(--p-models)}
.tw-scene.m .wl{font-size:12.5px} .tw-scene.m .ww{font-size:12px}
@keyframes sc-m-pct{0%,43%{opacity:0} 44%,78%{opacity:1} 79%,100%{opacity:0}}
@keyframes sc-m-note{0%,89%{opacity:0} 90%,99%{opacity:1} 100%{opacity:0}}

html.tw-motion[data-motion="full"] .tw-scene .m-pct{animation:sc-m-pct 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene .m-note{animation:sc-m-note 12s linear infinite}
.tw-scene.d:not(.inside) .q1{transform:translate(60px,144px)} .tw-scene.d:not(.inside) .ghost{transform:translate(60px,196px)} .tw-scene.d:not(.inside) .psg{transform:translate(635px,420px)} .tw-scene.d:not(.inside) .a1{transform:translate(910px,168px)} .tw-scene.d:not(.inside) .q2{transform:translate(60px,144px)}
@keyframes sc-f-q1{0%,3%{transform:translate(60px,144px);opacity:1} 14%,22%{transform:translate(370px,168px);opacity:1} 30%,44%{transform:translate(635px,168px);opacity:1} 52%,56%{transform:translate(910px,168px);opacity:1} 58%,100%{transform:translate(910px,168px);opacity:0}}
@keyframes sc-f-gst{0%,3%{transform:translate(60px,196px) scaleY(1);opacity:.85} 14%,22%{transform:translate(370px,220px) scaleY(1);opacity:.85} 30%{transform:translate(635px,220px) scaleY(1);opacity:.85} 36%,44%{transform:translate(635px,220px) scaleY(.25);opacity:.85} 52%,56%{transform:translate(910px,220px) scaleY(.25);opacity:.85} 58%,100%{transform:translate(910px,220px) scaleY(.25);opacity:0}}
@keyframes sc-f-psg{0%,32%{transform:translate(635px,420px);opacity:0} 33%{transform:translate(635px,420px);opacity:1} 40%,44%{transform:translate(635px,240px);opacity:1} 52%,56%{transform:translate(910px,240px);opacity:1} 58%,100%{transform:translate(910px,240px);opacity:0}}
@keyframes sc-f-a1{0%,56%{transform:translate(910px,168px);opacity:0} 58%,60%{transform:translate(910px,168px);opacity:1} 65%{transform:translate(910px,600px);opacity:1} 70%{transform:translate(300px,600px);opacity:1} 74%{transform:translate(300px,144px);opacity:1} 77%,86%{transform:translate(60px,144px);opacity:1} 90%,100%{transform:translate(60px,144px);opacity:0}}
@keyframes sc-f-q2{0%,80%{transform:translate(60px,144px);opacity:0} 82%{transform:translate(60px,144px);opacity:1} 88%,94%{transform:translate(370px,168px);opacity:1} 98%{transform:translate(60px,144px);opacity:1} 100%{transform:translate(60px,144px);opacity:0}}


@keyframes sc-f-gov{0%,13%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))} 14%,24%{stroke:var(--p-context);fill:color-mix(in srgb,var(--p-context) 14%,var(--bg))} 26%,87%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))} 88%,98%{stroke:var(--p-gov);fill:color-mix(in srgb,var(--p-gov) 16%,var(--bg))} 100%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))}}
@keyframes sc-f-ctx{0%,29%{stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule));fill:color-mix(in srgb,var(--p-context) 6%,var(--bg))} 30%,46%{stroke:var(--p-context);fill:color-mix(in srgb,var(--p-context) 14%,var(--bg))} 48%,100%{stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule));fill:color-mix(in srgb,var(--p-context) 6%,var(--bg))}}
@keyframes sc-f-doc{0%,31%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))} 32%,42%{stroke:var(--p-docs);fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg))} 44%,61%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))} 62%,70%{stroke:var(--p-docs);fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg))} 72%,100%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))}}
@keyframes sc-f-mdl{0%,51%{stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule));fill:color-mix(in srgb,var(--p-models) 6%,var(--bg))} 52%,62%{stroke:var(--p-models);fill:color-mix(in srgb,var(--p-models) 14%,var(--bg))} 64%,100%{stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule));fill:color-mix(in srgb,var(--p-models) 6%,var(--bg))}}
@keyframes sc-f-ldn{0%,31%{stroke:var(--sc-line);stroke-dasharray:3 4} 32%,42%{stroke:var(--p-docs);stroke-dasharray:none} 44%,100%{stroke:var(--sc-line);stroke-dasharray:3 4}}
@keyframes sc-f-lup{0%,61%{stroke:var(--sc-line);stroke-dasharray:3 4} 62%,70%{stroke:var(--p-docs);stroke-dasharray:none} 72%,100%{stroke:var(--sc-line);stroke-dasharray:3 4}}
@keyframes sc-f-tok{0%,14%{opacity:0} 16%,26%{opacity:1} 28%,100%{opacity:0}}
@keyframes sc-f-tno{0%,88%{opacity:0} 90%,98%{opacity:1} 100%{opacity:0}}
@keyframes sc-f-ttn{0%,40%{opacity:0} 42%,50%{opacity:1} 52%,100%{opacity:0}}
@keyframes sc-f-r1{0%,59%{opacity:0;transform:translateY(4px)} 62%,99%{opacity:1;transform:translateY(0)} 100%{opacity:0}}
@keyframes sc-f-r2{0%,91%{opacity:0;transform:translateY(4px)} 93%,99%{opacity:1;transform:translateY(0)} 100%{opacity:0}}
@keyframes sc-f-q2c{0%,89%{stroke:var(--rule)} 90%,99%{stroke:var(--p-gov)} 100%{stroke:var(--rule)}}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .q1{animation:sc-f-q1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .ghost{animation:sc-f-gst 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .psg{animation:sc-f-psg 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .a1{animation:sc-f-a1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .q2{animation:sc-f-q2 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .q2 .cd{animation:sc-f-q2c 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .gov rect{animation:sc-f-gov 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .ctx rect{animation:sc-f-ctx 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .doc rect{animation:sc-f-doc 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .mdl rect{animation:sc-f-mdl 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .link{animation:sc-f-ldn 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .link.up{animation:sc-f-lup 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .tag.ok{animation:sc-f-tok 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .tag.no{animation:sc-f-tno 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .tag.turns{animation:sc-f-ttn 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .rec1{animation:sc-f-r1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d:not(.inside) .rec2{animation:sc-f-r2 12s linear infinite}
.tw-scene.d.inside .q1{transform:translate(60px,134px)} .tw-scene.d.inside .ghost{transform:translate(60px,186px)} .tw-scene.d.inside .psg{transform:translate(340px,624px)} .tw-scene.d.inside .a1{transform:translate(775px,168px)} .tw-scene.d.inside .q2{transform:translate(60px,134px)}
@keyframes sc-i-q1{0%,3%{transform:translate(60px,134px);opacity:1} 14%,22%{transform:translate(340px,208px);opacity:1} 30%,44%{transform:translate(340px,428px);opacity:1} 52%,56%{transform:translate(775px,168px);opacity:1} 58%,100%{transform:translate(775px,168px);opacity:0}}
@keyframes sc-i-gst{0%,3%{transform:translate(60px,186px) scaleY(1);opacity:.85} 14%,22%{transform:translate(340px,260px) scaleY(1);opacity:.85} 30%{transform:translate(340px,480px) scaleY(1);opacity:.85} 36%,44%{transform:translate(340px,480px) scaleY(.25);opacity:.85} 52%,56%{transform:translate(775px,220px) scaleY(.25);opacity:.85} 58%,100%{transform:translate(775px,220px) scaleY(.25);opacity:0}}
@keyframes sc-i-psg{0%,32%{transform:translate(340px,624px);opacity:0} 33%{transform:translate(340px,624px);opacity:1} 40%,44%{transform:translate(340px,500px);opacity:1} 52%,56%{transform:translate(775px,240px);opacity:1} 58%,100%{transform:translate(775px,240px);opacity:0}}
@keyframes sc-i-a1{0%,56%{transform:translate(775px,168px);opacity:0} 58%,60%{transform:translate(775px,168px);opacity:1} 65%{transform:translate(775px,700px);opacity:1} 70%{transform:translate(20px,700px);opacity:1} 74%{transform:translate(20px,134px);opacity:1} 77%,86%{transform:translate(60px,134px);opacity:1} 90%,100%{transform:translate(60px,134px);opacity:0}}
@keyframes sc-i-q2{0%,80%{transform:translate(60px,134px);opacity:0} 82%{transform:translate(60px,134px);opacity:1} 88%,94%{transform:translate(340px,208px);opacity:1} 98%{transform:translate(60px,134px);opacity:1} 100%{transform:translate(60px,134px);opacity:0}}


@keyframes sc-i-gov{0%,13%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))} 14%,24%{stroke:var(--p-context);fill:color-mix(in srgb,var(--p-context) 14%,var(--bg))} 26%,87%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))} 88%,98%{stroke:var(--p-gov);fill:color-mix(in srgb,var(--p-gov) 16%,var(--bg))} 100%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))}}
@keyframes sc-i-ctx{0%,29%{stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule));fill:color-mix(in srgb,var(--p-context) 6%,var(--bg))} 30%,46%{stroke:var(--p-context);fill:color-mix(in srgb,var(--p-context) 14%,var(--bg))} 48%,100%{stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule));fill:color-mix(in srgb,var(--p-context) 6%,var(--bg))}}
@keyframes sc-i-doc{0%,31%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))} 32%,42%{stroke:var(--p-docs);fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg))} 44%,61%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))} 62%,70%{stroke:var(--p-docs);fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg))} 72%,100%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))}}
@keyframes sc-i-mdl{0%,51%{stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule));fill:color-mix(in srgb,var(--p-models) 6%,var(--bg))} 52%,62%{stroke:var(--p-models);fill:color-mix(in srgb,var(--p-models) 14%,var(--bg))} 64%,100%{stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule));fill:color-mix(in srgb,var(--p-models) 6%,var(--bg))}}
@keyframes sc-i-ldn{0%,31%{stroke:var(--sc-line);stroke-dasharray:3 4} 32%,42%{stroke:var(--p-docs);stroke-dasharray:none} 44%,100%{stroke:var(--sc-line);stroke-dasharray:3 4}}
@keyframes sc-i-lup{0%,61%{stroke:var(--sc-line);stroke-dasharray:3 4} 62%,70%{stroke:var(--p-docs);stroke-dasharray:none} 72%,100%{stroke:var(--sc-line);stroke-dasharray:3 4}}
@keyframes sc-i-tok{0%,14%{opacity:0} 16%,26%{opacity:1} 28%,100%{opacity:0}}
@keyframes sc-i-tno{0%,88%{opacity:0} 90%,98%{opacity:1} 100%{opacity:0}}
@keyframes sc-i-ttn{0%,40%{opacity:0} 42%,50%{opacity:1} 52%,100%{opacity:0}}
@keyframes sc-i-r1{0%,59%{opacity:0;transform:translateY(4px)} 62%,99%{opacity:1;transform:translateY(0)} 100%{opacity:0}}
@keyframes sc-i-r2{0%,91%{opacity:0;transform:translateY(4px)} 93%,99%{opacity:1;transform:translateY(0)} 100%{opacity:0}}
@keyframes sc-i-q2c{0%,89%{stroke:var(--rule)} 90%,99%{stroke:var(--p-gov)} 100%{stroke:var(--rule)}}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .q1{animation:sc-i-q1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .ghost{animation:sc-i-gst 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .psg{animation:sc-i-psg 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .a1{animation:sc-i-a1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .q2{animation:sc-i-q2 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .q2 .cd{animation:sc-i-q2c 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .gov rect{animation:sc-i-gov 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .ctx rect{animation:sc-i-ctx 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .doc rect{animation:sc-i-doc 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .mdl rect{animation:sc-i-mdl 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .link{animation:sc-i-ldn 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .link.up{animation:sc-i-lup 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .tag.ok{animation:sc-i-tok 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .tag.no{animation:sc-i-tno 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .tag.turns{animation:sc-i-ttn 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .rec1{animation:sc-i-r1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.d.inside .rec2{animation:sc-i-r2 12s linear infinite}
.tw-scene.m:not(.inside) .q1{transform:translate(40px,80px)} .tw-scene.m:not(.inside) .ghost{transform:translate(40px,132px)} .tw-scene.m:not(.inside) .psg{transform:translate(60px,958px)} .tw-scene.m:not(.inside) .a1{transform:translate(60px,1408px)} .tw-scene.m:not(.inside) .q2{transform:translate(40px,80px)}
@keyframes sc-mf-q1{0%,3%{transform:translate(40px,80px);opacity:1} 14%,22%{transform:translate(60px,508px);opacity:1} 30%,44%{transform:translate(60px,728px);opacity:1} 52%,56%{transform:translate(60px,1408px);opacity:1} 58%,100%{transform:translate(60px,1408px);opacity:0}}
@keyframes sc-mf-gst{0%,3%{transform:translate(40px,132px) scaleY(1);opacity:.85} 14%,22%{transform:translate(60px,560px) scaleY(1);opacity:.85} 30%{transform:translate(60px,780px) scaleY(1);opacity:.85} 36%,44%{transform:translate(60px,780px) scaleY(.25);opacity:.85} 52%,56%{transform:translate(60px,1460px) scaleY(.25);opacity:.85} 58%,100%{transform:translate(60px,1460px) scaleY(.25);opacity:0}}
@keyframes sc-mf-psg{0%,32%{transform:translate(60px,958px);opacity:0} 33%{transform:translate(60px,958px);opacity:1} 40%,44%{transform:translate(60px,800px);opacity:1} 52%,56%{transform:translate(60px,1480px);opacity:1} 58%,100%{transform:translate(60px,1480px);opacity:0}}
@keyframes sc-mf-a1{0%,56%{transform:translate(60px,1408px);opacity:0} 58%,60%{transform:translate(60px,1408px);opacity:1} 72%{transform:translate(60px,1408px);opacity:1} 74%{transform:translate(60px,1408px);opacity:0} 78%{transform:translate(40px,80px);opacity:0} 81%,86%{transform:translate(40px,80px);opacity:1} 90%,100%{transform:translate(40px,80px);opacity:0}}
@keyframes sc-mf-q2{0%,80%{transform:translate(40px,80px);opacity:0} 82%{transform:translate(40px,80px);opacity:1} 88%,94%{transform:translate(60px,508px);opacity:1} 98%{transform:translate(40px,80px);opacity:1} 100%{transform:translate(40px,80px);opacity:0}}
@keyframes sc-mf-rp{0%,73%{stroke:var(--sc-line);stroke-dasharray:none;stroke-dashoffset:0} 74%{stroke:var(--p-models);stroke-dasharray:6 6;stroke-dashoffset:0} 80%{stroke:var(--p-models);stroke-dasharray:6 6;stroke-dashoffset:-96} 81%,100%{stroke:var(--sc-line);stroke-dasharray:none;stroke-dashoffset:0}}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .wire.ret{animation:sc-mf-rp 12s linear infinite}
@keyframes sc-mf-gov{0%,13%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))} 14%,24%{stroke:var(--p-context);fill:color-mix(in srgb,var(--p-context) 14%,var(--bg))} 26%,87%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))} 88%,98%{stroke:var(--p-gov);fill:color-mix(in srgb,var(--p-gov) 16%,var(--bg))} 100%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))}}
@keyframes sc-mf-ctx{0%,29%{stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule));fill:color-mix(in srgb,var(--p-context) 6%,var(--bg))} 30%,46%{stroke:var(--p-context);fill:color-mix(in srgb,var(--p-context) 14%,var(--bg))} 48%,100%{stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule));fill:color-mix(in srgb,var(--p-context) 6%,var(--bg))}}
@keyframes sc-mf-doc{0%,31%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))} 32%,42%{stroke:var(--p-docs);fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg))} 44%,61%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))} 62%,70%{stroke:var(--p-docs);fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg))} 72%,100%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))}}
@keyframes sc-mf-mdl{0%,51%{stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule));fill:color-mix(in srgb,var(--p-models) 6%,var(--bg))} 52%,62%{stroke:var(--p-models);fill:color-mix(in srgb,var(--p-models) 14%,var(--bg))} 64%,100%{stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule));fill:color-mix(in srgb,var(--p-models) 6%,var(--bg))}}
@keyframes sc-mf-ldn{0%,31%{stroke:var(--sc-line);stroke-dasharray:3 4} 32%,42%{stroke:var(--p-docs);stroke-dasharray:none} 44%,100%{stroke:var(--sc-line);stroke-dasharray:3 4}}
@keyframes sc-mf-lup{0%,61%{stroke:var(--sc-line);stroke-dasharray:3 4} 62%,70%{stroke:var(--p-docs);stroke-dasharray:none} 72%,100%{stroke:var(--sc-line);stroke-dasharray:3 4}}
@keyframes sc-mf-tok{0%,14%{opacity:0} 16%,21%{opacity:1} 22%,100%{opacity:0}}
@keyframes sc-mf-tno{0%,88%{opacity:0} 90%,98%{opacity:1} 100%{opacity:0}}
@keyframes sc-mf-ttn{0%,36%{opacity:0} 38%,43%{opacity:1} 44%,100%{opacity:0}}
@keyframes sc-mf-r1{0%,59%{opacity:0;transform:translateY(4px)} 62%,99%{opacity:1;transform:translateY(0)} 100%{opacity:0}}
@keyframes sc-mf-r2{0%,91%{opacity:0;transform:translateY(4px)} 93%,99%{opacity:1;transform:translateY(0)} 100%{opacity:0}}
@keyframes sc-mf-q2c{0%,89%{stroke:var(--rule)} 90%,99%{stroke:var(--p-gov)} 100%{stroke:var(--rule)}}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .q1{animation:sc-mf-q1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .ghost{animation:sc-mf-gst 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .psg{animation:sc-mf-psg 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .a1{animation:sc-mf-a1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .q2{animation:sc-mf-q2 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .q2 .cd{animation:sc-mf-q2c 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .gov rect{animation:sc-mf-gov 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .ctx rect{animation:sc-mf-ctx 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .doc rect{animation:sc-mf-doc 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .mdl rect{animation:sc-mf-mdl 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .link{animation:sc-mf-ldn 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .link.up{animation:sc-mf-lup 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .tag.ok{animation:sc-mf-tok 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .tag.no{animation:sc-mf-tno 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .tag.turns{animation:sc-mf-ttn 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .rec1{animation:sc-mf-r1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m:not(.inside) .rec2{animation:sc-mf-r2 12s linear infinite}
.tw-scene.m.inside .q1{transform:translate(40px,80px)} .tw-scene.m.inside .ghost{transform:translate(40px,132px)} .tw-scene.m.inside .psg{transform:translate(80px,938px)} .tw-scene.m.inside .a1{transform:translate(60px,1418px)} .tw-scene.m.inside .q2{transform:translate(40px,80px)}
@keyframes sc-mi-q1{0%,3%{transform:translate(40px,80px);opacity:1} 14%,22%{transform:translate(80px,488px);opacity:1} 30%,44%{transform:translate(80px,708px);opacity:1} 52%,56%{transform:translate(60px,1418px);opacity:1} 58%,100%{transform:translate(60px,1418px);opacity:0}}
@keyframes sc-mi-gst{0%,3%{transform:translate(40px,132px) scaleY(1);opacity:.85} 14%,22%{transform:translate(80px,540px) scaleY(1);opacity:.85} 30%{transform:translate(80px,760px) scaleY(1);opacity:.85} 36%,44%{transform:translate(80px,760px) scaleY(.25);opacity:.85} 52%,56%{transform:translate(60px,1470px) scaleY(.25);opacity:.85} 58%,100%{transform:translate(60px,1470px) scaleY(.25);opacity:0}}
@keyframes sc-mi-psg{0%,32%{transform:translate(80px,938px);opacity:0} 33%{transform:translate(80px,938px);opacity:1} 40%,44%{transform:translate(80px,780px);opacity:1} 52%,56%{transform:translate(60px,1490px);opacity:1} 58%,100%{transform:translate(60px,1490px);opacity:0}}
@keyframes sc-mi-a1{0%,56%{transform:translate(60px,1418px);opacity:0} 58%,60%{transform:translate(60px,1418px);opacity:1} 72%{transform:translate(60px,1418px);opacity:1} 74%{transform:translate(60px,1418px);opacity:0} 78%{transform:translate(40px,80px);opacity:0} 81%,86%{transform:translate(40px,80px);opacity:1} 90%,100%{transform:translate(40px,80px);opacity:0}}
@keyframes sc-mi-q2{0%,80%{transform:translate(40px,80px);opacity:0} 82%{transform:translate(40px,80px);opacity:1} 88%,94%{transform:translate(80px,488px);opacity:1} 98%{transform:translate(40px,80px);opacity:1} 100%{transform:translate(40px,80px);opacity:0}}
@keyframes sc-mi-rp{0%,73%{stroke:var(--sc-line);stroke-dasharray:none;stroke-dashoffset:0} 74%{stroke:var(--p-models);stroke-dasharray:6 6;stroke-dashoffset:0} 80%{stroke:var(--p-models);stroke-dasharray:6 6;stroke-dashoffset:-96} 81%,100%{stroke:var(--sc-line);stroke-dasharray:none;stroke-dashoffset:0}}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .wire.ret{animation:sc-mi-rp 12s linear infinite}
@keyframes sc-mi-gov{0%,13%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))} 14%,24%{stroke:var(--p-context);fill:color-mix(in srgb,var(--p-context) 14%,var(--bg))} 26%,87%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))} 88%,98%{stroke:var(--p-gov);fill:color-mix(in srgb,var(--p-gov) 16%,var(--bg))} 100%{stroke:color-mix(in srgb,var(--p-gov) 45%,var(--rule));fill:color-mix(in srgb,var(--p-gov) 6%,var(--bg))}}
@keyframes sc-mi-ctx{0%,29%{stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule));fill:color-mix(in srgb,var(--p-context) 6%,var(--bg))} 30%,46%{stroke:var(--p-context);fill:color-mix(in srgb,var(--p-context) 14%,var(--bg))} 48%,100%{stroke:color-mix(in srgb,var(--p-context) 45%,var(--rule));fill:color-mix(in srgb,var(--p-context) 6%,var(--bg))}}
@keyframes sc-mi-doc{0%,31%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))} 32%,42%{stroke:var(--p-docs);fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg))} 44%,61%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))} 62%,70%{stroke:var(--p-docs);fill:color-mix(in srgb,var(--p-docs) 14%,var(--bg))} 72%,100%{stroke:color-mix(in srgb,var(--p-docs) 45%,var(--rule));fill:color-mix(in srgb,var(--p-docs) 6%,var(--bg))}}
@keyframes sc-mi-mdl{0%,51%{stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule));fill:color-mix(in srgb,var(--p-models) 6%,var(--bg))} 52%,62%{stroke:var(--p-models);fill:color-mix(in srgb,var(--p-models) 14%,var(--bg))} 64%,100%{stroke:color-mix(in srgb,var(--p-models) 45%,var(--rule));fill:color-mix(in srgb,var(--p-models) 6%,var(--bg))}}
@keyframes sc-mi-ldn{0%,31%{stroke:var(--sc-line);stroke-dasharray:3 4} 32%,42%{stroke:var(--p-docs);stroke-dasharray:none} 44%,100%{stroke:var(--sc-line);stroke-dasharray:3 4}}
@keyframes sc-mi-lup{0%,61%{stroke:var(--sc-line);stroke-dasharray:3 4} 62%,70%{stroke:var(--p-docs);stroke-dasharray:none} 72%,100%{stroke:var(--sc-line);stroke-dasharray:3 4}}
@keyframes sc-mi-tok{0%,14%{opacity:0} 16%,21%{opacity:1} 22%,100%{opacity:0}}
@keyframes sc-mi-tno{0%,88%{opacity:0} 90%,98%{opacity:1} 100%{opacity:0}}
@keyframes sc-mi-ttn{0%,36%{opacity:0} 38%,43%{opacity:1} 44%,100%{opacity:0}}
@keyframes sc-mi-r1{0%,59%{opacity:0;transform:translateY(4px)} 62%,99%{opacity:1;transform:translateY(0)} 100%{opacity:0}}
@keyframes sc-mi-r2{0%,91%{opacity:0;transform:translateY(4px)} 93%,99%{opacity:1;transform:translateY(0)} 100%{opacity:0}}
@keyframes sc-mi-q2c{0%,89%{stroke:var(--rule)} 90%,99%{stroke:var(--p-gov)} 100%{stroke:var(--rule)}}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .q1{animation:sc-mi-q1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .ghost{animation:sc-mi-gst 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .psg{animation:sc-mi-psg 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .a1{animation:sc-mi-a1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .q2{animation:sc-mi-q2 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .q2 .cd{animation:sc-mi-q2c 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .gov rect{animation:sc-mi-gov 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .ctx rect{animation:sc-mi-ctx 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .doc rect{animation:sc-mi-doc 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .mdl rect{animation:sc-mi-mdl 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .link{animation:sc-mi-ldn 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .link.up{animation:sc-mi-lup 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .tag.ok{animation:sc-mi-tok 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .tag.no{animation:sc-mi-tno 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .tag.turns{animation:sc-mi-ttn 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .rec1{animation:sc-mi-r1 12s linear infinite}
html.tw-motion[data-motion="full"] .tw-scene.m.inside .rec2{animation:sc-mi-r2 12s linear infinite}
