1414 < meta name ="publisher " content ="OpenHVX project " />
1515 < meta name ="copyright " content ="Apache-2.0 " />
1616
17-
1817 < link rel ="canonical " href ="https://openhvx.org/ " />
1918 < link rel ="alternate " hreflang ="en " href ="https://openhvx.org/ " />
2019
3938 < meta property ="og:locale " content ="en_US " />
4039 < meta property ="og:locale:alternate " content ="fr_FR " />
4140 < meta property ="og:url " content ="https://openhvx.org/ " />
42- <!-- TODO: mets une image 1200×630 absolue -->
4341 < meta property ="og:image " content ="https://openhvx.org/assets/logo.png " />
4442 < meta property ="og:image:secure_url " content ="https://openhvx.org/assets/logo.png " />
4543 < meta property ="og:image:width " content ="1200 " />
5149
5250 <!-- Twitter -->
5351 < meta name ="twitter:card " content ="summary_large_image " />
54- <!-- TODO: renseigne ton handle si tu en as un -->
5552 < meta name ="twitter:site " content ="@OpenHVX " />
5653 < meta name ="twitter:creator " content ="@OpenHVX " />
5754 < meta name ="twitter:title " content ="OpenHVX — Open-source IaaS for Hyper-V " />
5855 < meta name ="twitter:description " content ="A lean, self-hosted alternative to Azure Stack HCI / WAP. " />
5956 < meta name ="twitter:image " content ="https://openhvx.org/assets/logo.png " />
6057 < meta name ="twitter:image:alt " content ="OpenHVX — Open-source IaaS for Hyper-V " />
6158
62- <!-- Sitemap (complète aussi robots.txt) -->
59+ <!-- Sitemap -->
6360 < link rel ="sitemap " type ="application/xml " title ="Sitemap " href ="/sitemap.xml " />
64-
65- <!-- Structured Data (JSON-LD) -->
66- < script type ="application/ld+json ">
67- {
68- "@context" : "https://schema.org" ,
69- "@graph" : [
70- {
71- "@type" : "Organization" ,
72- "name" : "OpenHVX" ,
73- "url" : "https://openhvx.org/" ,
74- "logo" : "https://openhvx.org/assets/logo.png" ,
75- "sameAs" : [
76- "https://github.com/openhvx" ,
77- "https://github.com/orgs/openhvx/discussions"
78- ]
79- } ,
80- {
81- "@type" : "WebSite" ,
82- "name" : "OpenHVX" ,
83- "url" : "https://openhvx.org/" ,
84- "potentialAction" : {
85- "@type" : "ReadAction" ,
86- "target" : "https://openhvx.org/docs"
87- } ,
88- "inLanguage" : "en"
89- } ,
90- {
91- "@type" : "SoftwareApplication" ,
92- "name" : "OpenHVX" ,
93- "applicationCategory" : "Infrastructure as a Service" ,
94- "operatingSystem" : "Windows Server (Hyper-V)" ,
95- "softwareRequirements" : "Windows Server with Hyper-V; PowerShell; modern browser" ,
96- "isAccessibleForFree" : true ,
97- "offers" : {
98- "@type" : "Offer" ,
99- "price" : "0" ,
100- "priceCurrency" : "USD"
101- } ,
102- "downloadUrl" : "https://github.com/OpenHVX/openhvx-backend" ,
103- "publisher" : {
104- "@type" : "Organization" ,
105- "name" : "OpenHVX"
106- } ,
107- "url" : "https://openhvx.org/"
108- }
109- ]
110- }
111- </ script >
11261</ head >
11362
11463< body >
@@ -233,10 +182,11 @@ <h2>High-level architecture</h2>
233182
234183 < figure class ="arch-card ">
235184 < picture >
236- < source media ="(prefers-color-scheme: dark) " srcset ="assets/schema.openhvx.dark.png ">
185+ < source media ="(prefers-color-scheme: light) " srcset ="/assets/schema.openhvx.light.png ">
186+ < source media ="(prefers-color-scheme: dark) " srcset ="/assets/schema.openhvx.dark.png ">
237187 < img
238188 class ="arch-img "
239- src ="/assets/schema.openvhx .dark.png "
189+ src ="/assets/schema.openhvx .dark.png "
240190 alt ="OpenHVX high-level architecture diagram "
241191 loading ="lazy "
242192 decoding ="async "
@@ -340,26 +290,48 @@ <h2>Built by practitioners, for practitioners.</h2>
340290 </ div >
341291 </ footer >
342292
293+ <!-- JS -->
343294 < script >
344- document . getElementById ( 'y' ) . textContent = new Date ( ) . getFullYear ( ) ;
295+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
296+ // dynamic year
297+ const y = document . getElementById ( 'y' ) ;
298+ if ( y ) y . textContent = new Date ( ) . getFullYear ( ) ;
299+
300+ // Auto-label comparison table for mobile cards
301+ document . querySelectorAll ( '.cmp-table' ) . forEach ( table => {
302+ const headers = Array . from ( table . querySelectorAll ( 'thead th' ) ) . map ( th => th . textContent . trim ( ) ) ;
303+ table . querySelectorAll ( 'tbody tr' ) . forEach ( tr => {
304+ tr . querySelectorAll ( 'td' ) . forEach ( ( td , i ) => td . setAttribute ( 'data-label' , headers [ i ] || '' ) ) ;
305+ } ) ;
306+ } ) ;
307+ } ) ;
345308 </ script >
346309
310+ <!-- CSS -->
347311 < style >
348312 : root {
313+ /* Brand */
349314 --brand : # 06d6a0 ;
350315 --brand-dark : # 001a2c ;
351316 --brand-light : # 22c4b8 ;
352317
318+ /* Surfaces */
353319 --bg : var (--brand-dark );
354320 --bg-elev : # 0b2236 ;
355321 --text : # e6e8ec ;
356322 --muted : # a6adbb ;
357323 --border : # 1e2430 ;
358324 --shadow : 0 10px 30px rgba (0 , 0 , 0 , .35 );
359325
326+ /* Status */
360327 --ok : var (--brand );
361328 --maybe : # cbd5e1 ;
362329 --bad : # ef4444 ;
330+
331+ /* Timeline states */
332+ --state-done : # 06d6a0 ;
333+ --state-progress : # 22c4b8 ;
334+ --state-planned : # 7b8da6 ;
363335 }
364336
365337 @media (prefers-color-scheme : light) {
@@ -385,30 +357,34 @@ <h2>Built by practitioners, for practitioners.</h2>
385357 var (--bg );
386358 }
387359
388- /* Nav */
360+ /* -------- Nav -------- */
389361 .nav {
390362 max-width : 1100px ; margin : 0 auto; padding : 18px 20px ;
391- display : flex; align-items : center; justify-content : space-between;
363+ display : flex; align-items : center; justify-content : space-between; gap : 12 px ;
392364 }
393365 .nav .brand { display : flex; align-items : center; text-decoration : none; }
394366 .nav .brand img { height : 60px ; width : auto; }
395- .nav-links { display : flex; gap : 16 px ; align-items : center; flex-wrap : wrap; }
367+ .nav-links { display : flex; gap : 12 px ; align-items : center; flex-wrap : wrap; }
396368 .nav a { color : var (--muted ); text-decoration : none; }
397369 .nav a : hover { color : var (--text ); }
370+ .nav .btn .small { padding : 6px 10px ; font-size : 14px ; line-height : 1 ; }
371+ @media (max-width : 640px ){
372+ .nav { flex-direction : column; align-items : center; }
373+ .nav-links { justify-content : center; }
374+ }
398375
399- /* Hero */
376+ /* -------- Hero -------- */
400377 .hero {
401378 max-width : 1100px ; margin : 40px auto 0 ; padding : 40px 20px 0 ; text-align : center;
402379 }
403- .alt-badge {
404- display : inline-block; padding : 6px 10px ; border-radius : 999px ;
405- border : 1px solid var (--border );
406- background : rgba (6 , 214 , 160 , .10 );
407- color : # 9be8cf ; font-size : 13px ;
408- }
409380 .hero h1 { font-size : clamp (40px , 8vw , 68px ); margin : 12px 0 10px ; }
410381 .lead { max-width : 860px ; margin : 0 auto; font-size : 18px ; color : var (--muted ); }
411382 .cta { margin : 24px 0 ; display : flex; gap : 12px ; justify-content : center; flex-wrap : wrap; }
383+ .hero .cta .btn { flex : 0 1 230px ; justify-content : center; }
384+ @media (max-width : 420px ){
385+ .hero .cta { flex-direction : column; }
386+ .hero .cta .btn { width : 100% ; }
387+ }
412388
413389 .btn {
414390 display : inline-flex; align-items : center; gap : 8px ;
@@ -417,7 +393,6 @@ <h2>Built by practitioners, for practitioners.</h2>
417393 text-decoration : none; box-shadow : var (--shadow );
418394 font-weight : 500 ;
419395 }
420- .btn .small { padding : 8px 12px ; border-radius : 10px ; }
421396 .btn .primary {
422397 background : linear-gradient (135deg , var (--brand ), var (--brand-light ));
423398 color : var (--brand-dark ); font-weight : 700 ; border : none;
@@ -428,20 +403,16 @@ <h2>Built by practitioners, for practitioners.</h2>
428403 .badges { display : flex; gap : 10px ; justify-content : center; margin : 18px 0 0 ; opacity : .95 ; flex-wrap : wrap; }
429404 .badges img { height : 20px ; }
430405
431- /* Features */
406+ /* -------- Features -------- */
432407 .features { max-width : 1100px ; margin : 60px auto; padding : 0 20px ; }
433408 .features .grid {
434409 display : grid;
435410 gap : 20px ;
436411 grid-template-columns : repeat (3 , 1fr );
437412 align-items : stretch;
438413 }
439- @media (max-width : 900px ) {
440- .features .grid { grid-template-columns : repeat (2 , 1fr ); }
441- }
442- @media (max-width : 600px ) {
443- .features .grid { grid-template-columns : 1fr ; }
444- }
414+ @media (max-width : 900px ) { .features .grid { grid-template-columns : repeat (2 , 1fr ); } }
415+ @media (max-width : 600px ) { .features .grid { grid-template-columns : 1fr ; } }
445416 .features article {
446417 background : var (--bg-elev ); border : 1px solid var (--border );
447418 border-radius : 16px ; padding : 18px ; box-shadow : var (--shadow );
@@ -450,16 +421,17 @@ <h2>Built by practitioners, for practitioners.</h2>
450421 .features h3 { margin : 0 0 6px ; font-size : 18px ; }
451422 .features code { font-size : 13px ; opacity : .9 ; }
452423
453- /* Comparison */
424+ /* -------- Comparison -------- */
454425 .compare { max-width : 1100px ; margin : 50px auto 0 ; padding : 0 20px ; }
455426 .compare h2 { text-align : center; margin-bottom : 6px ; font-size : 28px ; }
456427 .compare .center { text-align : center; }
457- .compare . cmp-wrap {
428+ .cmp-wrap {
458429 margin-top : 14px ;
459430 background : var (--bg-elev );
460431 border : 1px solid var (--border );
461432 border-radius : 14px ; box-shadow : var (--shadow );
462433 overflow : hidden;
434+ overflow-x : auto; /* sécurité */
463435 }
464436 .cmp-table { width : 100% ; border-collapse : separate; border-spacing : 0 ; }
465437 .cmp-table thead th {
@@ -471,26 +443,40 @@ <h2>Built by practitioners, for practitioners.</h2>
471443 padding : 12px 14px ; font-size : 14px ; border-top : 1px solid rgba (255 , 255 , 255 , .04 );
472444 }
473445 .cmp-table tbody tr : nth-child (odd) td { background : rgba (255 , 255 , 255 , .02 ); }
474- .cmp-table td : nth-child (2 ){ width : 38% ; }
475- .cmp-table td : nth-child (3 ){ width : 38% ; }
476-
477- .yes { color : var (--ok ); }
478- .maybe { color : var (--maybe ); }
479- .no { color : var (--bad ); }
446+ .cmp-table td : nth-child (2 ), .cmp-table td : nth-child (3 ){ width : 38% ; }
447+ .yes { color : var (--ok ); } .maybe { color : var (--maybe ); } .no { color : var (--bad ); }
480448 .tiny { font-size : 12px ; margin : 10px 12px 14px ; }
481449
450+ /* Mobile card layout for the table */
482451 @media (max-width : 720px ){
483452 .cmp-table thead { display : none; }
484453 .cmp-table , .cmp-table tbody , .cmp-table tr , .cmp-table td { display : block; width : 100% ; }
485- .cmp-table tbody tr { border-top : 1px solid var (--border ); }
486- .cmp-table tbody td { border : none; }
454+ .cmp-table tbody tr {
455+ padding : 12px 14px ;
456+ border-top : 1px solid var (--border );
457+ background : transparent;
458+ }
459+ .cmp-table tbody tr : nth-child (odd){
460+ background : rgba (255 , 255 , 255 , .02 );
461+ border-radius : 12px ;
462+ }
463+ .cmp-table tbody td {
464+ padding : 8px 0 ;
465+ display : flex; gap : 10px ; justify-content : space-between; align-items : baseline;
466+ }
487467 .cmp-table tbody td ::before {
488468 content : attr (data-label);
489- display : block; font-weight : 600 ; color : var (--muted ); margin-bottom : 2px ;
469+ flex : 0 0 130px ;
470+ color : var (--muted );
471+ font-weight : 600 ;
490472 }
473+ .cmp-table tbody td : first-child {
474+ font-weight : 700 ; font-size : 15px ; padding-top : 0 ;
475+ }
476+ .cmp-table tbody td : first-child ::before { content : "" ; }
491477 }
492478
493- /* Architecture */
479+ /* -------- Architecture -------- */
494480 .arch { max-width : 1100px ; margin : 60px auto; padding : 0 20px ; text-align : center; }
495481 .arch h2 { font-size : 28px ; margin-bottom : 6px ; }
496482 .muted { color : var (--muted ); }
@@ -500,19 +486,17 @@ <h2>Built by practitioners, for practitioners.</h2>
500486 }
501487 .arch-img { display : block; width : 100% ; height : auto; }
502488
503- /* Roadmap (timeline) — tes règles existantes + états colorés (garde tel quel) */
489+ /* -------- Timeline -------- */
504490 .timeline { max-width : 1100px ; margin : 60px auto; padding : 0 20px ; }
505491 .timeline .tl-head { text-align : center; margin-bottom : 22px ; }
506492 .timeline h2 { font-size : 28px ; margin : 0 0 6px ; }
507493 .timeline .tl-legend { display : inline-flex; gap : 8px ; flex-wrap : wrap; justify-content : center; margin-top : 6px ; }
508-
509494 .t-badge {
510495 display : inline-flex; align-items : center; gap : 6px ;
511496 padding : 4px 10px ; border-radius : 999px ;
512497 font-size : 12px ; border : 1px solid var (--border );
513498 background : var (--bg-elev ); color : var (--text );
514499 }
515-
516500 .tl-list {
517501 position : relative; display : grid;
518502 grid-template-columns : repeat (5 , 1fr );
@@ -522,7 +506,6 @@ <h2>Built by practitioners, for practitioners.</h2>
522506 content : "" ; position : absolute; left : 0 ; right : 0 ; top : 16px ;
523507 height : 2px ; background : var (--border ); opacity : .9 ; pointer-events : none; z-index : 1 ;
524508 }
525-
526509 .tl-item {
527510 position : relative; background : var (--bg-elev );
528511 border : 1px solid var (--border ); border-radius : 14px ; box-shadow : var (--shadow ); padding : 14px ;
@@ -536,8 +519,7 @@ <h2>Built by practitioners, for practitioners.</h2>
536519 .tl-foot { display : flex; justify-content : center; margin-top : 2px ; }
537520 .tl-foot .btn .small { line-height : 1 ; }
538521
539- /* Bullets + états (visuels forts) */
540- : root { --state-done : # 06d6a0 ; --state-progress : # 22c4b8 ; --state-planned : # 7b8da6 ; }
522+ /* Bullets + states */
541523 .tl-item .tl-dot {
542524 position : absolute; top : -16px ; left : 50% ; width : 16px ; height : 16px ; border-radius : 50% ;
543525 border : 2px solid var (--brand ); background : var (--bg ); transform : translateX (-50% );
@@ -581,14 +563,12 @@ <h2>Built by practitioners, for practitioners.</h2>
581563 .tl-desc { font-size : 13px ; }
582564 }
583565
584- /* Callout */
566+ /* -------- Callout & Footer -------- */
585567 .callout {
586568 max-width : 980px ; margin : 60px auto; padding : 26px 20px ; text-align : center;
587569 background : linear-gradient (135deg , rgba (6 , 214 , 160 , .10 ), rgba (0 , 26 , 44 , .80 ));
588570 border : 1px solid var (--border ); border-radius : 18px ;
589571 }
590-
591- /* Footer */
592572 .footer {
593573 max-width : 1100px ; margin : 60px auto 30px ; padding : 0 20px ;
594574 display : grid; gap : 10px ; grid-template-columns : repeat (auto-fit, minmax (200px , 1fr ));
0 commit comments