5353# epc : focus-visible {outline : 2px solid var (--accent );outline-offset : 2px ;border-radius : 6px }
5454# epc .wrap {max-width : 1180px ;margin : 0 auto;padding : 0 24px }
5555
56- /* ---------- Top toolbar ---------- */
57- # epc .topbar {border-bottom : 1px solid var (--border )}
58- # epc .tb-in {display : flex;align-items : center;justify-content : space-between;gap : 14px ;padding : 15px 24px ;max-width : 1180px ;margin : 0 auto}
59- # epc .brand {font-family : var (--f-display );font-weight : 800 ;font-size : 21px ;letter-spacing : .2px ;white-space : nowrap;display : flex;align-items : baseline;gap : 12px }
60- # epc .brand b {background : var (--grad );-webkit-background-clip : text;background-clip : text;color : transparent;font-weight : 800 }
61- # epc .brand span {color : var (--text-3 );font-family : var (--f-mono );font-weight : 500 ;font-size : 10.5px ;letter-spacing : .18em ;text-transform : uppercase;transform : translateY (-1px )}
62- # epc .tb-right {display : flex;align-items : center;gap : 10px ;flex-wrap : wrap;justify-content : flex-end}
63- # epc .badge {font-family : var (--f-mono );font-size : 11.5px ;color : var (--text-2 );border : 1px solid var (--border );border-radius : 999px ;padding : 6px 13px ;background : var (--surface );white-space : nowrap}
56+ /* ---------- Segmented controls ---------- */
6457# epc .seg {display : inline-flex;border : 1px solid var (--border );border-radius : 10px ;overflow : hidden;background : var (--surface )}
6558# epc .seg button {background : transparent;color : var (--text-3 );border : none;font-family : var (--f-mono );font-size : 12px ;font-weight : 600 ;padding : 8px 13px ;cursor : pointer;transition : background .15s , color .15s }
6659# epc .seg button : hover {color : var (--text-2 )}
110103
111104/* ---------- Configurator ---------- */
112105# epc .conf-head {padding : 60px 24px 8px }
106+ # epc .conf-top {display : flex;align-items : center;justify-content : space-between;gap : 16px ;flex-wrap : wrap}
113107# epc .sec-h {font-family : var (--f-display );font-weight : 800 ;font-size : clamp (24px , 3.4vw , 36px );letter-spacing : -.015em ;line-height : 1.12 }
114108# epc .sec-sub {color : var (--text-2 );font-size : 15.5px ;margin-top : 12px ;max-width : 640px }
115109# epc .grid {display : grid;grid-template-columns : minmax (0 , 1fr ) 396px ;gap : 26px ;padding : 28px 24px 90px ;align-items : start;max-width : 1180px ;margin : 0 auto}
315309# epc footer .f-meta {display : flex;gap : 10px 22px ;flex-wrap : wrap;padding-top : 16px ;border-top : 1px solid var (--border );color : var (--text-3 )}
316310# epc footer .f-meta span {white-space : nowrap}
317311
318- /* ---------- Expiry overlay ---------- */
319- # epc .expired {position : fixed;inset : 0 ;background : rgba (6 , 7 , 10 , .96 );display : none;align-items : center;justify-content : center;text-align : center;padding : 24px ;z-index : 90 ;backdrop-filter : blur (8px )}
320- # epc .expired .show {display : flex}
321- # epc .expired .box {max-width : 440px }
322- # epc .expired .ic {width : 52px ;height : 52px ;border-radius : 14px ;margin : 0 auto 18px ;background : rgba (255 , 138 , 61 , .1 );border : 1px solid rgba (255 , 138 , 61 , .35 );display : flex;align-items : center;justify-content : center;color : var (--warn );font-family : var (--f-mono );font-size : 22px }
323- # epc .expired h3 {font-family : var (--f-display );font-size : 26px ;font-weight : 800 ;margin-bottom : 10px }
324- # epc .expired p {color : var (--text-2 );font-size : 15px }
325312
326313/* ---------- Reveal and toast ---------- */
327314# epc .reveal {opacity : 0 ;transform : translateY (16px );transition : opacity .6s ease, transform .6s ease}
334321 # epc .reveal {opacity : 1 ;transform : none}
335322}
336323@media print{
337- # epc .aura , # epc .gridbg , # epc .topbar . seg , # epc .hero-actions , # epc .presets , # epc .cta-col , # epc .matrix , # epc .toast {display : none!important }
324+ # epc .aura , # epc .gridbg , # epc .seg , # epc .hero-actions , # epc .presets , # epc .cta-col , # epc .matrix , # epc .toast {display : none!important }
338325 # epc {background : # fff ;color : # 111 }
339326 # epc .card , # epc .summary , # epc .cmp-card , # epc .dcard {border-color : # ddd ;background : # fff ;box-shadow : none}
340327 # epc .total {color : # 0092FF ;-webkit-text-fill-color : # 0092FF ;filter : none}
341328}
342329</ style >
343330
344331
345- < div class ="expired " id ="expired " role ="alertdialog " aria-modal ="true ">
346- < div class ="box ">
347- < div class ="ic "> !</ div >
348- < h3 data-i ="exp.h "> </ h3 >
349- < p data-i ="exp.p "> </ p >
350- </ div >
351- </ div >
352-
353- < header class ="topbar ">
354- < div class ="tb-in ">
355- < div class ="tb-right ">
356- < span class ="badge " id ="validBadge "> </ span >
357- < div class ="seg " id ="langSeg " role ="group " aria-label ="Language ">
358- < button data-lang ="en " class ="on "> EN</ button > < button data-lang ="es "> ES</ button >
359- </ div >
360- < div class ="seg " id ="curSeg " role ="group " aria-label ="Currency ">
361- < button data-cur ="USD " class ="on "> USD</ button > < button data-cur ="EUR "> EUR</ button >
362- </ div >
363- </ div >
364- </ div >
365- </ header >
366-
367332< section class ="hero wrap ">
368333 < div class ="eyebrow ">
369334 < span class ="pulse " aria-hidden ="true "> </ span >
370335 < span id ="preparedFor "> </ span >
371- < span class ="dot " aria-hidden ="true "> </ span >
372- < span class ="muted " id ="validEyebrow "> </ span >
373336 </ div >
374337 < h1 > Stack< span class ="gx "> Gres</ span > < br > < span class ="u " data-i ="hero.h1b "> </ span > </ h1 >
375338 < p class ="tagline " data-i ="hero.tag "> </ p >
@@ -399,7 +362,12 @@ <h1>Stack<span class="gx">Gres</span><br><span class="u" data-i="hero.h1b"></spa
399362</ section >
400363
401364< div class ="conf-head wrap " id ="configurator ">
402- < h2 class ="sec-h reveal " data-i ="conf.h "> </ h2 >
365+ < div class ="conf-top ">
366+ < h2 class ="sec-h reveal " data-i ="conf.h "> </ h2 >
367+ < div class ="seg " id ="curSeg " role ="group " aria-label ="Currency ">
368+ < button data-cur ="USD " class ="on "> USD</ button > < button data-cur ="EUR "> EUR</ button >
369+ </ div >
370+ </ div >
403371 < p class ="sec-sub reveal " data-i ="conf.sub "> </ p >
404372</ div >
405373
@@ -534,7 +502,6 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
534502 ======================================================================= */
535503const LINK = {
536504 client : "" ,
537- validUntil : "" , // empty = no expiry; per-client links set ?valid=YYYY-MM-DD
538505 currency : "USD" , // "USD" | "EUR" (each currency has its own official price list)
539506 lang : "en" ,
540507 proposalEmail : "cesarcp@ongres.com"
@@ -597,10 +564,7 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
597564en : {
598565 title :"StackGres Enterprise Pricing" ,
599566 "tb.tag" :"Enterprise Pricing" ,
600- "exp.h" :"This estimate has expired" ,
601- "exp.p" :"The shared link is no longer active. Please contact OnGres for an updated quote." ,
602567 prep :"Prepared for {c}" , prepNeutral :"StackGres Enterprise subscription" ,
603- validBadge :"Valid until {d}" , validShort :"Estimate valid until {d}" ,
604568 "hero.h1a" :"StackGres" ,
605569 "hero.h1b" :"Priced in real time." ,
606570 "hero.tag" :"Enterprise Postgres on Kubernetes." ,
@@ -687,13 +651,12 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
687651 slaCols :[ "SLA level" , "Response" , "Update" , "Resolution" , "Issue-based" , "Dedicated telephone" ] ,
688652 slaNames :[ "10x5" , "24x7 Enterprise" , "24x7 Premium" ] ,
689653 legalMain :"StackGres is a product of OnGres, Inc. (Delaware, USA). Prices are per core-year in the selected currency, per the official OnGres price list (USD or EUR). This calculator produces an indicative estimate; the binding offer is the formal proposal issued by OnGres." ,
690- legalValid :"This estimate is valid until {d}. After that date, please contact OnGres for an updated quote." ,
691654 legalConf :"Confidential: prepared for the named recipient only." ,
692655 mSubject :"StackGres formal proposal request" , mFor :"Client" ,
693656 mCores :"Cores under support" , mBilled :"Billable cores" , mPg :"PostgreSQL Support" , mPro :"Proactive package" ,
694657 mSg :"StackGres edition" , mCons :"Consulting" , mMkt :"Marketing Agreement" , mHours :"Consulting hours included" ,
695- mTotal :"Estimated total per year" , mValid : "Estimate valid until" , mNone :"none" , mOff :"not included" ,
696- pdfTitle :"PRICING ESTIMATE" , pdfPrep :"Prepared for" , pdfDate :"Date" , pdfValid : "Valid until" ,
658+ mTotal :"Estimated total per year" , mNone :"none" , mOff :"not included" ,
659+ pdfTitle :"PRICING ESTIMATE" , pdfPrep :"Prepared for" , pdfDate :"Date" ,
697660 pdfEnv :"Environment: {n} billable cores" ,
698661 pdfPgT :"PostgreSQL Support (OnGres)" , pdfSgT :"StackGres Enterprise licensing" ,
699662 pdfCols :[ "Bracket" , "Cores" , "Unit" , "Subtotal" ] , pdfColsPro :[ "Fee" , "Fee subtotal" ] ,
@@ -708,10 +671,7 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
708671es : {
709672 title :"Precios StackGres Enterprise" ,
710673 "tb.tag" :"Precios Enterprise" ,
711- "exp.h" :"Esta estimación ha caducado" ,
712- "exp.p" :"El enlace compartido ya no está activo. Contacta con OnGres para una cotización actualizada." ,
713674 prep :"Preparado para {c}" , prepNeutral :"Suscripción StackGres Enterprise" ,
714- validBadge :"Válido hasta el {d}" , validShort :"Estimación válida hasta el {d}" ,
715675 "hero.h1a" :"StackGres" ,
716676 "hero.h1b" :"Con precio en tiempo real." ,
717677 "hero.tag" :"Postgres enterprise sobre Kubernetes." ,
@@ -798,13 +758,12 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
798758 slaCols :[ "Nivel de SLA" , "Respuesta" , "Actualización" , "Resolución" , "Basado en incidencias" , "Teléfono dedicado" ] ,
799759 slaNames :[ "10x5" , "24x7 Enterprise" , "24x7 Premium" ] ,
800760 legalMain :"StackGres es un producto de OnGres, Inc. (Delaware, EE. UU.). Los precios son por core-año en la moneda seleccionada, según la lista de precios oficial de OnGres (USD o EUR). Esta calculadora ofrece una estimación indicativa; la oferta vinculante es la propuesta formal emitida por OnGres." ,
801- legalValid :"Esta estimación es válida hasta el {d}. Pasada esa fecha, contacte con OnGres para una cotización actualizada." ,
802761 legalConf :"Confidencial: preparado únicamente para el destinatario indicado." ,
803762 mSubject :"Solicitud de propuesta formal StackGres" , mFor :"Cliente" ,
804763 mCores :"Cores bajo soporte" , mBilled :"Cores facturables" , mPg :"Soporte PostgreSQL" , mPro :"Paquete Proactive" ,
805764 mSg :"Edición StackGres" , mCons :"Consultoría" , mMkt :"Acuerdo de Marketing" , mHours :"Horas de consultoría incluidas" ,
806- mTotal :"Total anual estimado" , mValid : "Estimación válida hasta" , mNone :"ninguna" , mOff :"no incluido" ,
807- pdfTitle :"ESTIMACIÓN DE PRECIOS" , pdfPrep :"Preparado para" , pdfDate :"Fecha" , pdfValid : "Válido hasta" ,
765+ mTotal :"Total anual estimado" , mNone :"ninguna" , mOff :"no incluido" ,
766+ pdfTitle :"ESTIMACIÓN DE PRECIOS" , pdfPrep :"Preparado para" , pdfDate :"Fecha" ,
808767 pdfEnv :"Entorno: {n} cores facturables" ,
809768 pdfPgT :"Soporte PostgreSQL (OnGres)" , pdfSgT :"Licencia StackGres Enterprise" ,
810769 pdfCols :[ "Tramo" , "Cores" , "Unidad" , "Subtotal" ] , pdfColsPro :[ "Cuota" , "Subtotal cuota" ] ,
@@ -835,11 +794,6 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
835794 } ) . format ( v ) ;
836795}
837796const PR = ( ) => CFG . price [ state . cur ] ;
838- function dateNice ( iso ) {
839- const d = new Date ( iso + "T23:59:59" ) ;
840- if ( isNaN ( d ) ) return iso ;
841- return d . toLocaleDateString ( state . lang === "es" ? "es-ES" : "en-US" , { year :"numeric" , month :"short" , day :"numeric" } ) ;
842- }
843797function slaTimes ( slaKey ) {
844798 const s = CFG . sla [ slaKey ] , i = state . lang === "es" ? 1 : 0 ;
845799 return { r :s . response [ i ] , u :s . update [ i ] , x :s . resolution [ i ] , phone :s . phone } ;
@@ -903,22 +857,13 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
903857 document . documentElement . lang = state . lang ;
904858 document . title = t ( "title" ) ;
905859 document . querySelectorAll ( "[data-i]" ) . forEach ( el => { el . textContent = t ( el . dataset . i ) ; } ) ;
906- if ( LINK . validUntil ) {
907- const vd = dateNice ( LINK . validUntil ) ;
908- $ ( "#validBadge" ) . textContent = tpl ( t ( "validBadge" ) , { d :vd } ) ;
909- $ ( "#validEyebrow" ) . textContent = tpl ( t ( "validShort" ) , { d :vd } ) ;
910- } else {
911- $ ( "#validBadge" ) . style . display = "none" ;
912- $ ( "#validEyebrow" ) . textContent = "" ;
913- }
914860 $ ( "#preparedFor" ) . textContent = LINK . client ? tpl ( t ( "prep" ) , { c :LINK . client } ) : t ( "prepNeutral" ) ;
915861 $ ( "#cta" ) . textContent = t ( "cta" ) ;
916862 $ ( "#pdfBtn" ) . textContent = t ( "pdfBtn" ) ;
917863 $ ( "#copyBtn" ) . textContent = t ( "copy" ) ;
918864 $ ( "#minNotice" ) . textContent = t ( "minNotice" ) ;
919865 if ( $ ( "#edNotice" ) ) $ ( "#edNotice" ) . textContent = t ( "edSwitched" ) ;
920866 $ ( "#serverHint" ) . textContent = t ( "serverHint" ) ;
921- document . querySelectorAll ( "#langSeg button" ) . forEach ( b => b . classList . toggle ( "on" , b . dataset . lang === state . lang ) ) ;
922867 document . querySelectorAll ( "#curSeg button" ) . forEach ( b => b . classList . toggle ( "on" , b . dataset . cur === state . cur ) ) ;
923868 if ( $ ( "#valueChips" ) ) $ ( "#valueChips" ) . innerHTML = t ( "valueChips" ) . map ( v => `<span class="cloud">${ v } </span>` ) . join ( "" ) ;
924869}
@@ -1141,7 +1086,7 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
11411086
11421087 tween ( r . total ) ;
11431088 $ ( "#perNote" ) . textContent = t ( "sumNote" ) ;
1144- $ ( "#fineprint" ) . textContent = t ( "fine1" ) + ( LINK . validUntil ? " " + tpl ( t ( "legalValid" ) , { d : dateNice ( LINK . validUntil ) } ) : "" ) ;
1089+ $ ( "#fineprint" ) . textContent = t ( "fine1" ) ;
11451090 syncUrl ( ) ;
11461091}
11471092
@@ -1164,7 +1109,6 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
11641109 const doc = new jsPDF ( { unit :"mm" , format :"a4" } ) ;
11651110 const r = compute ( ) ;
11661111 const W = doc . internal . pageSize . getWidth ( ) , H = doc . internal . pageSize . getHeight ( ) , M = 16 ;
1167- const vd = LINK . validUntil ? dateNice ( LINK . validUntil ) : "" ;
11681112 const today = new Date ( ) . toLocaleDateString ( state . lang === "es" ?"es-ES" :"en-US" , { year :"numeric" , month :"short" , day :"numeric" } ) ;
11691113 let y = 0 ;
11701114
@@ -1177,7 +1121,6 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
11771121 doc . text ( t ( "pdfTitle" ) , M , 22 ) ;
11781122 doc . setTextColor ( 155 , 166 , 180 ) ; doc . setFontSize ( 9 ) ;
11791123 doc . text ( t ( "pdfDate" ) + ": " + today , W - M , 13 , { align :"right" } ) ;
1180- if ( vd ) doc . text ( t ( "pdfValid" ) + ": " + vd , W - M , 19 , { align :"right" } ) ;
11811124 if ( LINK . client ) doc . text ( t ( "pdfPrep" ) + ": " + LINK . client , W - M , 25 , { align :"right" } ) ;
11821125
11831126 y = 48 ;
@@ -1277,7 +1220,7 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
12771220 y += 3 ;
12781221 doc . setDrawColor ( 4 , 223 , 253 ) ; doc . setLineWidth ( 0.6 ) ; doc . line ( M , y , W - M , y ) ; y += 7 ;
12791222 doc . setTextColor ( 120 , 132 , 145 ) ; doc . setFontSize ( 7.8 ) ;
1280- const legal = doc . splitTextToSize ( t ( "legalMain" ) + ( vd ? " " + tpl ( t ( "legalValid" ) , { d : vd } ) : "" ) + " " + t ( "legalConf" ) , W - 2 * M ) ;
1223+ const legal = doc . splitTextToSize ( t ( "legalMain" ) + " " + t ( "legalConf" ) , W - 2 * M ) ;
12811224 doc . text ( legal , M , y ) ;
12821225
12831226 doc . setFillColor ( 8 , 9 , 12 ) ; doc . rect ( 0 , H - 12 , W , 12 , "F" ) ;
@@ -1321,7 +1264,6 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
13211264
13221265 $ ( "#mkt" ) . addEventListener ( "change" , e => { state . mkt = e . target . checked ; render ( ) ; } ) ;
13231266 $ ( "#curSeg" ) . addEventListener ( "click" , e => { const b = e . target . closest ( "button" ) ; if ( b ) { state . cur = b . dataset . cur ; applyStatic ( ) ; render ( ) ; } } ) ;
1324- $ ( "#langSeg" ) . addEventListener ( "click" , e => { const b = e . target . closest ( "button" ) ; if ( b ) { state . lang = b . dataset . lang ; applyStatic ( ) ; render ( ) ; } } ) ;
13251267
13261268 $ ( "#copyBtn" ) . addEventListener ( "click" , ( ) => {
13271269 const done = ( ) => showToast ( t ( "copied" ) ) ;
@@ -1343,7 +1285,6 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
13431285 b += t ( "mCons" ) + ": " + ( state . pkg > 0 ? state . pkg + " h = " + fmt ( r . consulting ) : t ( "mNone" ) ) + nl ;
13441286 if ( r . totalHours > 0 ) b += t ( "mHours" ) + ": " + r . totalHours + " h" + nl ;
13451287 b += t ( "mTotal" ) + ": " + fmt ( r . total ) + nl ;
1346- if ( LINK . validUntil ) b += t ( "mValid" ) + ": " + dateNice ( LINK . validUntil ) + nl ;
13471288 b += nl + location . href ;
13481289 location . href = "mailto:" + LINK . proposalEmail + "?subject=" + subject + "&body=" + encodeURIComponent ( b ) ;
13491290 } ) ;
@@ -1353,7 +1294,6 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
13531294( function init ( ) {
13541295 const P = new URLSearchParams ( location . search ) ;
13551296 if ( P . get ( "client" ) ) LINK . client = P . get ( "client" ) . slice ( 0 , 80 ) ;
1356- if ( P . get ( "valid" ) && / ^ \d { 4 } - \d { 2 } - \d { 2 } $ / . test ( P . get ( "valid" ) ) ) LINK . validUntil = P . get ( "valid" ) ;
13571297 if ( P . get ( "lang" ) === "es" || P . get ( "lang" ) === "en" ) LINK . lang = P . get ( "lang" ) ;
13581298 if ( ( P . get ( "cur" ) || "" ) . toUpperCase ( ) === "EUR" ) LINK . currency = "EUR" ;
13591299 state . lang = LINK . lang ; state . cur = LINK . currency ;
@@ -1369,8 +1309,6 @@ <h3 class="h3s reveal" data-i="cmp.slaH"></h3>
13691309 wire ( ) ;
13701310 render ( ) ;
13711311
1372- if ( LINK . validUntil && new Date ( ) > new Date ( LINK . validUntil + "T23:59:59" ) ) $ ( "#expired" ) . classList . add ( "show" ) ;
1373-
13741312 if ( "IntersectionObserver" in window ) {
13751313 const io = new IntersectionObserver ( entries => {
13761314 entries . forEach ( en => { if ( en . isIntersecting ) { en . target . classList . add ( "in" ) ; io . unobserve ( en . target ) ; } } ) ;
0 commit comments