daemon-sec-lotl

DÆMONBins: GTFOBins × LOLBAS × WADComs × LOOBins in one filterable catalog
git clone https://git.daemon-sec.xyz/daemon-sec-lotl.git
Log | Files | Refs | Submodules | README | LICENSE

HeroDeck.astro (10207B)


      1 ---
      2 import Icon from './Icon.astro';
      3 import facets from '../data/facets.json';
      4 import { SOURCES, SOURCE_META, SOURCE_ROUTE } from '../lib/taxonomy';
      5 import { url } from '../lib/url';
      6 
      7 /**
      8  * The home hero — a dark signal-field plate carrying the glitching, decoding
      9  * DÆMONBINS wordmark and an inverse (cream) ledger of the five collections. The
     10  * ledger's active deck cycles on a loop and takes over on hover/focus
     11  * (scripts/hero.ts); the stat counters roll up from zero on load. The whole
     12  * thing is server-rendered and fully legible with JS off — the script only
     13  * adds the motion, and it all collapses under prefers-reduced-motion.
     14  *
     15  * Ported in spirit from daemon-sec.xyz's HeroLoop (the deck-lighting inverse
     16  * plate) — rebuilt as progressive enhancement over static markup rather than a
     17  * canvas cinematic, so it stays framework-free and degrades cleanly.
     18  */
     19 const c = facets.counts;
     20 const decks = SOURCES.map((id, i) => {
     21   const s = SOURCE_META[id];
     22   return {
     23     n: String(i + 1).padStart(2, '0'),
     24     tag: s.tag,
     25     label: s.label,
     26     accent: s.accent,
     27     blurb: s.blurb,
     28     count: c.bySource[id] ?? 0,
     29     route: SOURCE_ROUTE[id],
     30   };
     31 });
     32 const WORDMARK = 'DÆMONBINS';
     33 ---
     34 <section class="hero-deck plate bleed" data-hero aria-label="DÆMONBins">
     35   <canvas class="hero-deck__field" data-fuzz aria-hidden="true"></canvas>
     36   <span class="banner__scrim" aria-hidden="true"></span>
     37   <span class="grain grain--local" aria-hidden="true"></span>
     38   <span class="scanlines" aria-hidden="true"></span>
     39 
     40   <div class="wrap hero-deck__body">
     41     <div class="hero-deck__lede">
     42       <p class="eyebrow" style="--acc: var(--love);">
     43         <span class="eyebrow__n">00</span>
     44         <span class="eyebrow__mark">^:</span>
     45         <span>DÆMONBins</span>
     46         <span class="eyebrow__rule" aria-hidden="true"></span>
     47       </p>
     48 
     49       <h1 class="hero-deck__wordmark glitch" data-text={WORDMARK} data-unscramble={WORDMARK}>{WORDMARK}</h1>
     50       <p class="hero-deck__tagline">the Off-the-Land Almanac</p>
     51       <p class="hero-deck__blurb">
     52         GTFOBins, LOLBAS, WADComs and LOOBins, with DÆMON's command references.
     53         Filter by access, environment and execution context. Configure templates and keep useful commands close.
     54       </p>
     55 
     56       <div class="hero-deck__actions">
     57         <a class="btn" href={url('catalog')}><Icon name="terminal" /> Open the catalog</a>
     58         <button class="btn btn--ghost" data-search-open type="button"><Icon name="search" /> Search</button>
     59         <span class="hero-deck__hint">or press <kbd>/</kbd></span>
     60       </div>
     61 
     62       <div class="hero-deck__stats">
     63         <div class="hero-deck__stat" style="--acc: var(--love);">
     64           <b data-count={c.techniques}>{c.techniques.toLocaleString()}</b><span>Techniques</span>
     65         </div>
     66         <div class="hero-deck__stat" style="--acc: var(--foam);">
     67           <b data-count={c.tools}>{c.tools.toLocaleString()}</b><span>Tools</span>
     68         </div>
     69         <div class="hero-deck__stat" style="--acc: var(--gold);">
     70           <b data-count={c.added}>{c.added.toLocaleString()}</b><span>DÆMON new</span>
     71         </div>
     72       </div>
     73     </div>
     74 
     75     <aside class="hero-deck__panel plate-dawn corners" data-hero-panel>
     76       <div class="hero-deck__panelhead">
     77         <span class="hero-deck__paneleyebrow"><span class="mark">^:</span> The collections</span>
     78         <span class="hero-deck__panellive">
     79           <span class="dmn-dot" style="--dot: var(--pine);"><i class="dmn-anim-pulse"></i><u class="dmn-anim-ring"></u></span>
     80           live
     81         </span>
     82       </div>
     83 
     84       <p class="hero-deck__deckblurb" data-hero-blurb>{decks[0].blurb}</p>
     85 
     86       <ul class="hero-deck__list">
     87         {decks.map((d, i) => (
     88           <li>
     89             <a
     90               class:list={['hero-deck__row', i === 0 && 'is-lit']}
     91               href={url(d.route)}
     92               style={`--acc: var(--${d.accent});`}
     93               data-hero-row={i}
     94               data-blurb={d.blurb}
     95               aria-current={i === 0 ? 'true' : undefined}
     96             >
     97               <span class="hero-deck__rn">{d.n}</span>
     98               <span class="hero-deck__rtag">{d.tag}</span>
     99               <span class="hero-deck__rname">{d.label}</span>
    100               <span class="hero-deck__rcount">{d.count.toLocaleString()}</span>
    101               <span class="hero-deck__rarrow" aria-hidden="true">→</span>
    102             </a>
    103           </li>
    104         ))}
    105       </ul>
    106     </aside>
    107   </div>
    108 </section>
    109 
    110 <style>
    111   .hero-deck {
    112     position: relative;
    113     overflow: hidden;
    114     background: var(--plate);
    115     border-bottom: 1px solid var(--fg);
    116   }
    117   .hero-deck__field { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
    118   /* The scanlines/grain ride over the field; the body is lifted above them. */
    119   .hero-deck > .scanlines { opacity: 0.4; }
    120 
    121   .hero-deck__body {
    122     position: relative;
    123     z-index: 2;
    124     display: grid;
    125     gap: 2rem 3.5rem;
    126     align-items: center;
    127     padding-block: clamp(2.4rem, 6vw, 4.6rem);
    128   }
    129   @media (min-width: 960px) {
    130     .hero-deck__body { grid-template-columns: minmax(0, 1fr) minmax(340px, 430px); }
    131   }
    132 
    133   /* ---- Left: the lede ---------------------------------------------------- */
    134   .hero-deck__wordmark {
    135     margin-top: 1.1rem;
    136     font-family: var(--font-wordmark);
    137     font-weight: 850;
    138     font-stretch: 68%;
    139     text-transform: uppercase;
    140     font-size: clamp(2.7rem, 8.5vw, 5.6rem);
    141     line-height: 0.9;
    142     letter-spacing: -0.005em;
    143     color: var(--fg);
    144   }
    145   .hero-deck__tagline {
    146     margin-top: 0.5rem;
    147     font-family: var(--font-lockup);
    148     font-style: italic;
    149     font-size: clamp(1.15rem, 2.6vw, 1.7rem);
    150     line-height: 1.1;
    151     color: var(--foam);
    152   }
    153   .hero-deck__blurb {
    154     margin-top: 1.1rem;
    155     max-width: 48ch;
    156     color: var(--fg-dim);
    157     font-size: 0.98rem;
    158     line-height: 1.55;
    159     text-wrap: pretty;
    160   }
    161   .hero-deck__actions {
    162     margin-top: 1.5rem;
    163     display: flex;
    164     flex-wrap: wrap;
    165     align-items: center;
    166     gap: 0.7rem;
    167   }
    168   .hero-deck__hint {
    169     font-family: var(--font-mono);
    170     font-size: 11.5px;
    171     letter-spacing: 0.04em;
    172     color: var(--fg-faint);
    173   }
    174   .hero-deck__hint kbd {
    175     font-family: var(--font-mono);
    176     border: 1px solid var(--rule-hi);
    177     padding: 0.05rem 0.35rem;
    178     color: var(--fg-dim);
    179   }
    180 
    181   .hero-deck__stats {
    182     margin-top: 1.9rem;
    183     padding-top: 1.3rem;
    184     border-top: 1px solid var(--rule);
    185     display: grid;
    186     grid-template-columns: repeat(3, auto);
    187     justify-content: start;
    188     gap: 0 clamp(1.6rem, 4vw, 3rem);
    189   }
    190   .hero-deck__stat b {
    191     display: block;
    192     font-family: var(--font-display);
    193     font-weight: 800;
    194     font-stretch: 84%;
    195     font-size: clamp(1.7rem, 3.4vw, 2.5rem);
    196     letter-spacing: -0.03em;
    197     line-height: 1;
    198     color: var(--acc);
    199     font-variant-numeric: tabular-nums;
    200   }
    201   .hero-deck__stat span {
    202     display: block;
    203     margin-top: 0.35rem;
    204     font-family: var(--font-mono);
    205     font-size: 10px;
    206     letter-spacing: 0.16em;
    207     text-transform: uppercase;
    208     color: var(--fg-faint);
    209   }
    210 
    211   /* ---- Right: the inverse deck ledger ------------------------------------ */
    212   .hero-deck__panel {
    213     background: var(--base);
    214     border: 1px solid var(--fg);
    215     padding: 1.2rem 1.35rem 0.6rem;
    216     color: var(--fg);
    217   }
    218   .hero-deck__panelhead {
    219     display: flex;
    220     align-items: center;
    221     justify-content: space-between;
    222     gap: 1rem;
    223     padding-bottom: 0.7rem;
    224     border-bottom: 1px solid var(--rule);
    225     font-family: var(--font-mono);
    226     font-size: 10px;
    227     letter-spacing: 0.16em;
    228     text-transform: uppercase;
    229     color: var(--fg-faint);
    230   }
    231   .hero-deck__paneleyebrow .mark { color: var(--iris); margin-right: 0.35rem; }
    232   .hero-deck__panellive { display: inline-flex; align-items: center; gap: 0.5rem; }
    233 
    234   .hero-deck__deckblurb {
    235     margin: 0;
    236     padding: 0.9rem 0 1rem;
    237     font-size: 0.86rem;
    238     line-height: 1.5;
    239     color: var(--fg-dim);
    240     /* Clamp to four lines so the cycling copy holds a constant height and the
    241        ledger below it never jumps as the active deck changes. */
    242     display: -webkit-box;
    243     -webkit-line-clamp: 4;
    244     line-clamp: 4;
    245     -webkit-box-orient: vertical;
    246     overflow: hidden;
    247     min-height: calc(0.86rem * 1.5 * 4 + 1.9rem);
    248   }
    249 
    250   .hero-deck__list { list-style: none; margin: 0; padding: 0; }
    251   .hero-deck__row {
    252     position: relative;
    253     display: grid;
    254     grid-template-columns: auto auto minmax(0, 1fr) auto auto;
    255     align-items: center;
    256     gap: 0.7rem;
    257     padding: 0.72rem 0.2rem 0.72rem 0.9rem;
    258     border-top: 1px solid var(--rule);
    259     color: var(--fg);
    260     text-decoration: none;
    261     transition: background-color 220ms ease;
    262   }
    263   .hero-deck__row::before {
    264     content: '';
    265     position: absolute;
    266     left: 0; top: 0; bottom: 0;
    267     width: 2px;
    268     background: var(--acc);
    269     transform: scaleY(0);
    270     transform-origin: 50% 50%;
    271     transition: transform 260ms var(--ease-in);
    272   }
    273   .hero-deck__row.is-lit::before,
    274   .hero-deck__row:hover::before { transform: scaleY(1); }
    275   .hero-deck__row.is-lit,
    276   .hero-deck__row:hover { background: var(--wash-strong); }
    277 
    278   .hero-deck__rn {
    279     font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em;
    280     color: var(--fg-faint); transition: color 200ms ease;
    281   }
    282   .hero-deck__rtag {
    283     font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em;
    284     text-transform: uppercase; color: var(--acc);
    285   }
    286   .hero-deck__rname {
    287     font-family: var(--font-display); font-weight: 700; font-stretch: 86%;
    288     font-size: 1.02rem; letter-spacing: -0.01em; text-transform: uppercase;
    289     color: var(--fg); transition: color 200ms ease; min-width: 0;
    290     overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    291   }
    292   .hero-deck__rcount {
    293     font-family: var(--font-mono); font-size: 11px; color: var(--fg-faint);
    294     font-variant-numeric: tabular-nums;
    295   }
    296   .hero-deck__rarrow { color: var(--acc); transition: transform 250ms var(--ease); }
    297   .hero-deck__row.is-lit .hero-deck__rn { color: var(--acc); }
    298   .hero-deck__row.is-lit .hero-deck__rname { color: var(--acc); }
    299   .hero-deck__row.is-lit .hero-deck__rarrow,
    300   .hero-deck__row:hover .hero-deck__rarrow { transform: translateX(4px); }
    301 </style>