@@ -168,346 +168,4 @@ <h1 class="post-heading__title">Your Degree is in Hand. Your Future Isn't.</h1>
168168 </ div >
169169 </ div >
170170 </ section >
171- </ main >
172-
173- <!--
174- ============================================================
175- PART 1: THE ADVANCED CSS
176- Handles the precise layout, synchronized animations, and interactive tooltips.
177- ============================================================
178- -->
179- < style >
180- .journey-section-final {
181- padding-block : clamp (4rem , 8vw , 6rem );
182- background-color : var (--spruce-footer-color-background , # f8f9fa );
183- overflow-x : hidden;
184- }
185-
186- .journey-wrapper {
187- position : relative;
188- max-width : 64rem ;
189- margin : 4rem auto;
190- }
191-
192- /* --- The Track and Progress Bar --- */
193- .journey-track-base ,
194- .journey-track-progress {
195- position : absolute;
196- top : 24px ; /* Vertically center with the 48px node */
197- height : 4px ;
198- z-index : 1 ;
199- }
200-
201- .journey-track-base {
202- background-color : var (--spruce-base-color-border , # e0e0e0 );
203- /* **THE FIX**: Start and end exactly at the node centers */
204- left : 0 ;
205- width : 100% ;
206- }
207-
208- .journey-track-progress {
209- background-color : var (--spruce-base-color-primary , # e53935 );
210- left : 0 ;
211- width : 0 ; /* JS will control this */
212- }
213-
214- /* --- The Milestones and their Content --- */
215- .journey-milestones {
216- display : flex;
217- justify-content : space-between;
218- position : relative;
219- }
220-
221- .journey-milestone {
222- position : absolute; /* Positioned precisely along the track */
223- top : 0 ;
224- display : flex;
225- flex-direction : column;
226- align-items : center;
227- width : 120px ;
228- transform : translateX (-50% );
229- cursor : default;
230- }
231-
232- .journey-milestone__node {
233- width : 48px ;
234- height : 48px ;
235- border-radius : 50% ;
236- background-color : var (--spruce-base-color-background , # fff );
237- border : 4px solid var (--spruce-base-color-border , # e0e0e0 );
238- display : flex;
239- align-items : center;
240- justify-content : center;
241- z-index : 2 ; /* Sits above the track */
242- transition : border-color 0.3s ease, transform 0.3s ease;
243- }
244-
245- .journey-milestone__icon {
246- font-size : 1.5rem ;
247- color : var (--spruce-base-color-text , # 555 );
248- transition : color 0.3s ease;
249- }
250-
251- .journey-milestone__title {
252- font-size : 0.85rem ;
253- font-weight : 600 ;
254- color : var (--spruce-base-color-text );
255- margin-top : 0.75rem ;
256- text-align : center;
257- line-height : 1.4 ;
258- opacity : 0.7 ;
259- transition : opacity 0.3s ease;
260- }
261-
262- /* --- Refined Tooltip Styles --- */
263- .journey-milestone__tooltip {
264- position : absolute;
265- bottom : 100% ;
266- margin-bottom : 1rem ;
267- width : 180px ;
268- background-color : var (--spruce-base-color-heading , # 111 );
269- color : var (--spruce-base-color-background , # fff );
270- padding : 0.6rem 0.8rem ;
271- border-radius : var (--spruce-border-radius , 8px );
272- text-align : center;
273- font-size : 0.85rem ;
274- line-height : 1.5 ;
275- opacity : 0 ; /* Hidden by default */
276- transform : translateY (10px );
277- pointer-events : none;
278- transition : opacity 0.3s ease, transform 0.3s ease;
279- z-index : 10 ;
280- }
281-
282- /* Tooltip arrow */
283- .journey-milestone__tooltip ::after {
284- content : '' ;
285- position : absolute;
286- top : 100% ; left : 50% ; margin-left : -8px ;
287- border : 8px solid transparent;
288- border-top-color : var (--spruce-base-color-heading , # 111 );
289- }
290-
291- /* --- ACTIVE STATE STYLES (Toggled by JS) --- */
292- .journey-milestone .is-active .journey-milestone__node {
293- border-color : var (--spruce-base-color-primary , # e53935 );
294- transform : scale (1.1 );
295- }
296- .journey-milestone .is-active .journey-milestone__icon {
297- color : var (--spruce-base-color-primary , # e53935 );
298- }
299- .journey-milestone .is-active .journey-milestone__title {
300- opacity : 1 ;
301- }
302- .journey-milestone .is-active .journey-milestone__tooltip {
303- opacity : 1 ;
304- transform : translateY (0 );
305- }
306-
307- /* --- Duration markers between nodes --- */
308- .journey-duration {
309- position : absolute;
310- top : 52px ; /* Position below the track */
311- transform : translateX (-50% );
312- font-size : 0.75rem ;
313- font-weight : 700 ;
314- color : var (--spruce-base-color-text );
315- background-color : var (--spruce-footer-color-background , # f8f9fa );
316- padding : 0.1rem 0.5rem ;
317- }
318-
319- /* --- Responsive Styles --- */
320- @media (max-width : 992px ) {
321- .journey-wrapper { display : flex; }
322- .journey-track-container { width : 48px ; }
323- .journey-track-base , .journey-track-progress {
324- top : 0 ; left : 50% ; right : auto; width : 4px ; height : 100% ;
325- }
326- .journey-milestones { flex-direction : column; width : 100% ; }
327- .journey-milestone {
328- flex-direction : row; align-items : center; width : auto;
329- transform : none; padding : 0 0 6rem 1.5rem ;
330- position : absolute; left : 0 ;
331- }
332- .journey-milestone : last-child { padding-bottom : 0 ; }
333- .journey-milestone__node { margin-right : 1.5rem ; }
334- .journey-milestone__title { text-align : left; }
335- .journey-milestone__tooltip {
336- left : 100% ; top : 50% ; transform : translateY (-50% ) translateX (10px ); margin : 0 0 0 1rem ;
337- }
338- .journey-milestone__tooltip ::after {
339- top : 50% ; left : -16px ; transform : translateY (-50% );
340- border-color : transparent var (--spruce-base-color-heading , # 111 ) transparent transparent;
341- }
342- .journey-duration {
343- top : 50% ; left : -25px ; transform : translate (-100% , -50% );
344- }
345- }
346- </ style >
347-
348-
349- <!--
350- ============================================================
351- PART 2: THE HTML STRUCTURE
352- Data attributes now control positioning for perfect sync.
353- ============================================================
354- -->
355- < div class ="section journey-section-final ">
356- < div class ="container ">
357- < div class ="section-title ">
358- < h2 class ="h2 "> My Journey: The Story Behind the Guide</ h2 >
359- < p class ="lead "> From a clueless graduate to a professional at a global firm—this is the path that inspired this project.</ p >
360- </ div >
361-
362- < div id ="journey-wrapper " class ="journey-wrapper ">
363- < div class ="journey-track-container ">
364- < div class ="journey-track-base "> </ div >
365- < div id ="journey-progress " class ="journey-track-progress "> </ div >
366- </ div >
367-
368- < div id ="journey-milestones " class ="journey-milestones ">
369- <!-- Milestone 1 -->
370- < div class ="journey-milestone " data-position ="0 ">
371- < div class ="journey-milestone__node "> < span class ="journey-milestone__icon "> < i class ="ph-duotone ph-flag "> </ i > </ span > </ div >
372- < h3 class ="journey-milestone__title "> Journey Begins</ h3 >
373- < div class ="journey-milestone__tooltip "> < p > Graduated in 2017, jobless and unsure of the next step.</ p > </ div >
374- </ div >
375-
376- <!-- Milestone 2 -->
377- < div class ="journey-milestone " data-position ="25 ">
378- < div class ="journey-milestone__node "> < span class ="journey-milestone__icon "> < i class ="ph-duotone ph-buildings "> </ i > </ span > </ div >
379- < h3 class ="journey-milestone__title "> Bangalore</ h3 >
380- < div class ="journey-milestone__tooltip "> < p > Took a leap of faith to immerse myself in India's tech hub.</ p > </ div >
381- </ div >
382-
383- <!-- Milestone 3 -->
384- < div class ="journey-milestone " data-position ="50 ">
385- < div class ="journey-milestone__node "> < span class ="journey-milestone__icon "> < i class ="ph-duotone ph-code "> </ i > </ span > </ div >
386- < h3 class ="journey-milestone__title "> JSpiders</ h3 >
387- < div class ="journey-milestone__tooltip "> < p > Began intensive training in Core Java and SQL.</ p > </ div >
388- </ div >
389-
390- <!-- Milestone 4 -->
391- < div class ="journey-milestone " data-position ="75 ">
392- < div class ="journey-milestone__node "> < span class ="journey-milestone__icon "> < i class ="ph-duotone ph-rocket-launch "> </ i > </ span > </ div >
393- < h3 class ="journey-milestone__title "> First Job</ h3 >
394- < div class ="journey-milestone__tooltip "> < p > Secured my first role at a startup. A crucial start.</ p > </ div >
395- </ div >
396-
397- <!-- Milestone 5 -->
398- < div class ="journey-milestone " data-position ="100 ">
399- < div class ="journey-milestone__node "> < span class ="journey-milestone__icon "> < i class ="ph-duotone ph-seal-check "> </ i > </ span > </ div >
400- < h3 class ="journey-milestone__title "> Goal Achieved!</ h3 >
401- < div class ="journey-milestone__tooltip "> < p > Joined Accenture in March 2023, inspiring this guide.</ p > </ div >
402- </ div >
403- </ div >
404-
405- <!-- Duration markers are now generated by JavaScript -->
406- < div id ="journey-durations " class ="journey-durations "> </ div >
407- </ div >
408- </ div >
409- </ div >
410-
411-
412- <!--
413- ============================================================
414- PART 3: THE JAVASCRIPT CONTROLLER
415- This script powers the animation and triggers the tooltips in perfect sync.
416- ============================================================
417- -->
418- < script >
419- document . addEventListener ( "DOMContentLoaded" , function ( ) {
420- const journeyWrapper = document . getElementById ( 'journey-wrapper' ) ;
421- if ( ! journeyWrapper ) return ;
422-
423- const progressBar = document . getElementById ( 'journey-progress' ) ;
424- const milestones = Array . from ( document . querySelectorAll ( '.journey-milestone' ) ) ;
425- const durationsContainer = document . getElementById ( 'journey-durations' ) ;
426-
427- let animationFrameId ;
428- let hasAnimated = false ;
429- const totalAnimationTime = 10 ; // seconds for one full loop
430-
431- // --- Setup Function ---
432- const setupJourney = ( ) => {
433- const durations = [ 4 , 6 , 4 , 54 ] ; // Months between steps
434- let cumulativePosition = 0 ;
435-
436- milestones . forEach ( ( ms , index ) => {
437- const position = parseInt ( ms . dataset . position , 10 ) ;
438- const isVertical = window . innerWidth <= 992 ;
439-
440- // Position milestone nodes
441- ms . style [ isVertical ? 'top' : 'left' ] = `${ position } %` ;
442-
443- // Create and position duration markers
444- if ( index < durations . length ) {
445- const nextPosition = parseInt ( milestones [ index + 1 ] . dataset . position , 10 ) ;
446- const midPoint = position + ( nextPosition - position ) / 2 ;
447-
448- const durationEl = document . createElement ( 'div' ) ;
449- durationEl . className = 'journey-duration' ;
450- durationEl . textContent = `+${ durations [ index ] } Months` ;
451- durationEl . style [ isVertical ? 'top' : 'left' ] = `${ midPoint } %` ;
452- durationsContainer . appendChild ( durationEl ) ;
453- }
454- } ) ;
455- } ;
456-
457-
458- // --- Animation Controller ---
459- const animateJourney = ( ) => {
460- let startTime = null ;
461-
462- function step ( timestamp ) {
463- if ( ! startTime ) startTime = timestamp ;
464- const elapsedTime = ( timestamp - startTime ) / 1000 ;
465- let progress = ( elapsedTime / totalAnimationTime ) * 100 ;
466-
467- if ( progress >= 100 ) {
468- progress = 100 ;
469- setTimeout ( ( ) => {
470- startTime = null ;
471- milestones . forEach ( ms => ms . classList . remove ( 'is-active' ) ) ;
472- if ( hasAnimated ) requestAnimationFrame ( step ) ;
473- } , 2000 ) ;
474- } else {
475- animationFrameId = requestAnimationFrame ( step ) ;
476- }
477-
478- const isVertical = window . innerWidth <= 992 ;
479- const progressProperty = isVertical ? 'height' : 'width' ;
480- progressBar . style [ progressProperty ] = `${ progress } %` ;
481-
482- milestones . forEach ( ms => {
483- const milestonePosition = parseInt ( ms . dataset . position , 10 ) ;
484- if ( progress >= milestonePosition ) {
485- ms . classList . add ( 'is-active' ) ;
486- } else {
487- ms . classList . remove ( 'is-active' ) ;
488- }
489- } ) ;
490- }
491-
492- animationFrameId = requestAnimationFrame ( step ) ;
493- } ;
494-
495- // --- Initialization ---
496- const observer = new IntersectionObserver ( ( entries ) => {
497- entries . forEach ( entry => {
498- if ( entry . isIntersecting && ! hasAnimated ) {
499- hasAnimated = true ;
500- animateJourney ( ) ;
501- observer . unobserve ( journeyWrapper ) ;
502- }
503- } ) ;
504- } , { threshold : 0.4 } ) ;
505-
506- setupJourney ( ) ; // Position elements correctly on load
507- observer . observe ( journeyWrapper ) ;
508- window . addEventListener ( 'resize' , ( ) => {
509- durationsContainer . innerHTML = '' ; // Clear old durations
510- setupJourney ( ) ; // Recalculate positions on resize
511- } ) ;
512- } ) ;
513- </ script >
171+ </ main >
0 commit comments