NOX Grid
aadmin2026-10-04 发布区块5 阅读

NOX Grid

类型:区块 · 收录:2026-10-04 12:33:20 · 源码:会员可见

[hidecontent type="vip1"]
Build a single standalone file named index.html: a full-viewport (100vh) dark-mode website FOOTER section, mobile responsive, using plain HTML + CSS + vanilla JS only (no frameworks, no build step). Reproduce every value below exactly.

=====================================================================
1. HEAD / FONTS
=====================================================================
- <!doctype html>, lang="en", meta charset utf-8, meta viewport "width=device-width, initial-scale=1".
- <title>NOX Footer</title>
- Google Fonts: preconnect to https://fonts.googleapis.com and https://fonts.gstatic.com (crossorigin), then load:
  https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;800&display=swap
- Body font stack: "Inter", "Helvetica Neue", Arial, sans-serif; -webkit-font-smoothing: antialiased; overflow-x: hidden.

=====================================================================
2. DESIGN TOKENS (:root)
=====================================================================
--bg: #000000
--fg: #f1efe9                          (off-white text + logo)
--muted: rgba(241, 239, 233, 0.62)     (links, tagline, copyright)
--line: rgba(241, 239, 233, 0.28)      (dashed grid lines)
--dash: 6px   --gap: 6px               (dash length / gap length)
--gutter: clamp(16px, 4.3vw, 56px)     (left/right page margin)
--cols: 4                              (becomes 2 on mobile)

Global reset: * { box-sizing: border-box; margin: 0; padding: 0 }. html, body background var(--bg), color var(--fg). Links: color inherit, no underline.

=====================================================================
3. HTML STRUCTURE (in this DOM order)
=====================================================================
<footer class="footer" id="footer">
  <div class="footer__media" aria-hidden="true">
    <video id="bg-video" muted playsinline preload="auto"
      src="https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260929_040915_1684f4cf-061d-4833-8ed5-854d812ec5df.mp4"></video>
  </div>
  <div class="grid-lines" id="grid-lines" aria-hidden="true">
    <span class="grid-lines__h"></span>
  </div>
  <div class="footer__inner" id="footer-inner">
    COLUMN 1 (div.col.col--brand):
      a.brand href="#" aria-label="NOX home" containing:
        - an inline SVG logo, class="brand__mark", viewBox="0 0 256 256", fill="none", aria-hidden="true", with ONE path:
          d="M 128 192 L 128 256 L 64.5 256 L 32 223 L 0 192 L 0 128 L 64 128 Z M 256 192 L 256 256 L 192.5 256 L 160 223 L 128 192 L 128 128 L 192 128 Z M 128 64 L 128 128 L 64.5 128 L 32 95 L 0 64 L 0 0 L 64 0 Z M 256 64 L 256 128 L 192.5 128 L 160 95 L 128 64 L 128 0 L 192 0 Z"
          The path is filled WHITE via CSS: .brand__mark path { fill: var(--fg) }
        - span.split with text "NOX"
      p.tagline.split  -> "The verifiable engine for autonomous work"
      p.copy.split     -> "© 2026 NOX Grid, Inc."
    COLUMN 2 (nav.col aria-label="Systems"): h3.split "Systems"; ul with 3 li > a.link.split href="#": "Pipeline", "Workflows", "Controls"
    COLUMN 3 (nav.col aria-label="Network"): h3.split "Network"; links "Story", "Culture", "Inquire"
    COLUMN 4 (nav.col aria-label="Knowledge"): h3.split "Knowledge"; links "Insights", "Field Manuals", "News"
  </div>
</footer>

=====================================================================
4. LAYOUT CSS
=====================================================================
.footer: position relative; height 100vh AND height 100svh (svh overrides); min-height 620px; width 100%; overflow hidden; background var(--bg); isolation isolate; display flex; flex-direction column.

.footer__inner (text area, sits at the TOP): position relative; z-index 2; margin 0 var(--gutter); padding-top clamp(84px, 13.5vh, 120px); display grid; grid-template-columns repeat(var(--cols), 1fr); text-align center.
.col: padding 0 12px.

.footer__media (video area, sits BELOW the text, fills the remaining height): position relative; order 2; flex 1 1 auto; min-height 0; margin-top clamp(16px, 3vh, 40px); z-index 0; pointer-events none.
.footer__media video: position absolute; inset 0; width 100%; height 100%; object-fit cover; object-position 50% 0% (anchored to the TOP of the video so the rock spires near the top of the frame are always visible and never behind the text); display block; feathered top edge with mask-image (and -webkit-mask-image): linear-gradient(to bottom, transparent 0%, #000 10%).
The video must NOT loop and must NOT autoplay by attribute; JS plays it once, and it holds on its final frame.

=====================================================================
5. TYPOGRAPHY
=====================================================================
.brand: display inline-flex; align-items center; gap 14px; font-weight 800; font-size clamp(30px, 3vw, 40px); letter-spacing -0.04em; line-height 1.
.brand__mark: display block; width 1.02em; height 1.02em; flex none.
.tagline: margin 22px auto 0; max-width 15em; font-size 15px; line-height 1.35; color var(--muted).
.copy: margin-top 22px; font-size 15px; color var(--muted).
.col h3: font-size 18px; font-weight 500; letter-spacing -0.01em; margin-top 2px; color var(--fg).
.col ul: list-style none; margin-top 14px.   .col li + li: margin-top 13px.
.link: position relative; display inline-block; font-size 15px; color var(--muted); padding-bottom 2px; transition color .3s ease.

=====================================================================
6. DASHED GRID LINES (generated in code, NOT part of the video)
=====================================================================
.grid-lines: position absolute; inset 0; z-index 1; pointer-events none.

Horizontal line .grid-lines__h: position absolute; left 0; right 0; top clamp(36px, 6.3vh, 56px); height 1px;
  background: repeating-linear-gradient(to right, var(--line) 0 var(--dash), transparent var(--dash) calc(var(--dash) + var(--gap)));
  transform-origin left center; initial transform scaleX(0).

Vertical lines .grid-lines__v (created by JS): position absolute; top 0; width 1px; fallback height 60%;
  background: repeating-linear-gradient(to bottom, var(--line) 0 var(--dash), transparent var(--dash) calc(var(--dash) + var(--gap)));
  mask-image (and -webkit-): linear-gradient(to bottom, #000 0%, #000 72%, transparent 100%)  (fades out at the bottom);
  initial clip-path: inset(0 0 100% 0)  (hidden, draws downward).

=====================================================================
7. ANIMATIONS (all triggered by adding class "is-live" to .footer)
=====================================================================
a) Horizontal line draws left to right:
   .is-live .grid-lines__h { transition: transform 1.6s cubic-bezier(.65,.05,.25,1); transform: scaleX(1) }
b) Vertical lines draw top to bottom, staggered 90ms per line (JS sets transition-delay = index*90ms):
   .is-live .grid-lines__v { transition: clip-path 1.8s cubic-bezier(.65,.05,.25,1); clip-path: inset(0 0 0 0) }
c) Logo mark: initial opacity .35 + transform rotate(-24deg) scale(.45); transition transform 1s cubic-bezier(.2,.9,.2,1), opacity .6s ease.
   .is-live .brand__mark { opacity 1; transform none }  (rotates and grows into place).
d) Per-character text reveal on every .split element:
   .split .w { display inline-block; white-space nowrap }  (keeps words from breaking mid-word)
   .split .ch { display inline-block; opacity 0; filter blur(6px); transform translateY(4px) }
   .is-live .split .ch { transition: opacity .5s ease, filter .6s ease, transform .6s cubic-bezier(.2,.9,.2,1); transition-delay: calc(var(--d, 0) * 1ms); opacity 1; filter blur(0); transform none }
e) Link hover / focus-visible:
   .link::before = "→" (content "2192"); position absolute; right 100%; top 0; margin-right 6px; opacity 0; transform translateX(-6px); transition opacity .3s ease, transform .35s cubic-bezier(.2,.9,.2,1).
   .link::after = 1px underline; position absolute; left 0; right 0; bottom 0; height 1px; background currentColor; transform scaleX(0); transform-origin right center; transition transform .4s cubic-bezier(.2,.9,.2,1).
   On :hover and :focus-visible: color var(--fg); outline none; ::before opacity 1 and transform none (arrow slides in from the left); ::after transform scaleX(1) with transform-origin left center (underline draws left to right, and retracts to the right on mouse-out).
f) Background video: plays once from time 0 when the footer enters view. It's an ink-engraving desert (dunes + rock spires + crescent moon) that assembles itself left to right with a shard/particle edge on pure black, then holds still.

=====================================================================
8. JAVASCRIPT (vanilla, inline at end of body, wrapped in an IIFE)
=====================================================================
1) Character split: for each .col (index ci), keep a counter i starting at 0 that continues across all .split elements inside that column. For each .split element: store its text in aria-label, empty it, split the text on spaces; between words append a plain text node " "; wrap each word in <span class="w" aria-hidden="true">; inside it, one <span class="ch"> per character with CSS variable --d = 250 + ci*180 + i*22 (i increments per character, and once more after each word).
2) buildLines(): remove existing .grid-lines__v; count = parseInt of the --cols CSS variable read from getComputedStyle(document.documentElement) (fallback 4); read the bounding rects of #footer-inner and #footer; left = inner.left - footer.left; w = inner.width; stop = media.offsetTop + media.offsetHeight * 0.12 (media = .footer__media). For k = 0..count create span.grid-lines__v with style.left = round(left + w*k/count) px, style.height = round(stop) px, style.transitionDelay = k*90 ms; append to #grid-lines. This gives count+1 lines on the column boundaries (5 on desktop, 3 on mobile), stopping just inside the top of the video.
   Call once on load, and on window resize (debounced 120ms).
3) start(): if the footer already has is-live, return; otherwise requestAnimationFrame(() => add "is-live"); set video.currentTime = 0 inside try/catch; call video.play() and swallow any rejected promise.
4) IntersectionObserver on #footer with threshold 0.35: when it intersects, call start() and disconnect. If IntersectionObserver isn't supported, call start() immediately.

=====================================================================
9. RESPONSIVE
=====================================================================
@media (max-width: 720px):
  :root { --cols: 2 }
  .footer__inner { row-gap: 40px; padding-top: 84px }
  .col--brand { grid-column: 1 / -1; margin: 0 1px; padding: 12px 12px 16px; background: var(--bg) }  (brand block goes full width on top and its black background hides the centre dashed line behind it)
  .footer { height: auto; min-height: 100vh; min-height: 100svh }  (can grow past one screen if the content needs it)
  .footer__media { min-height: 380px }
  Result: brand on top, then a 2-column grid (Systems | Network, then Knowledge below).

@media (max-width: 480px):
  .footer__media { min-height: 260px }
  .footer__inner { row-gap: 32px; padding-top: 72px }
  .brand { font-size: 30px }
  .tagline, .copy, .link { font-size: 14px }
  .col h3 { font-size: 16px }
  .col li + li { margin-top: 10px }

@media (prefers-reduced-motion: reduce):
  .grid-lines__h, .grid-lines__v, .brand__mark, .split .ch { transition: none !important; transform: none !important; clip-path: none !important; opacity: 1 !important; filter: none !important }

=====================================================================
10. ACCEPTANCE CHECKS
=====================================================================
- Desktop at 1268x792: 4 centred columns; the 5 vertical dashed lines land at about x = 55, 344, 634, 924, 1213 px; the horizontal dashed line sits about 50px from the top.
- The text never overlaps the video: the text block ends above where the video area begins, and the rock spires show at the top of the video area.
- Everything is on pure black (#000); text and logo are off-white #f1efe9; links are muted until hovered.
- Use the CloudFront video URL exactly as given, with no local copy and no loop.
[/hidecontent]
← 返回首页