@@ -68,27 +68,31 @@ function SessionBlock({
6868 session : SingaporeSession
6969 isFirst : boolean
7070} ) {
71- // On xl+ with a single speaker we float the card into the bottom-right of
72- // the description so prose flows around it. Multi-speaker sessions keep
73- // the regular "speakers below" layout at every breakpoint .
74- const floatSpeaker =
71+ // On xl+ with a single speaker we slot the card next to the last two
72+ // paragraphs of the description so it sits in the bottom-right corner.
73+ // Multi-speaker sessions keep the regular "speakers below" layout.
74+ const sideSpeaker =
7575 session . speakers . length === 1 ? session . speakers [ 0 ] : null
7676
7777 return (
7878 < article >
79- < Hr className = { isFirst ? "mt-8 lg:mt-12" : "mt-12 lg:mt-16" } />
80- < SessionHeader session = { session } className = "px-2 pt-8 sm:px-3 lg:pt-12" />
79+ < Hr
80+ className = {
81+ isFirst ? "mt-8 lg:mt-12 xl:mt-0" : "mt-12 lg:mt-16 xl:mt-0"
82+ }
83+ />
84+ < SessionHeader session = { session } className = "px-2 py-8 sm:px-3 lg:py-12" />
8185 { session . description && (
8286 < >
83- < Hr className = "mt-10 2xl:mt-16" />
87+ < Hr className = "mt-10 2xl:mt-16 xl:mt-0 " />
8488 < SessionDescription
8589 description = { session . description }
86- floatSpeaker = { floatSpeaker }
90+ sideSpeaker = { sideSpeaker }
8791 />
8892 </ >
8993 ) }
9094 { session . speakers . length > 0 && (
91- < div className = { floatSpeaker ? "xl:hidden" : undefined } >
95+ < div className = { sideSpeaker ? "xl:hidden" : undefined } >
9296 < Hr />
9397 < SessionSpeakers
9498 speakers = { session . speakers }
@@ -102,38 +106,47 @@ function SessionBlock({
102106
103107function SessionDescription ( {
104108 description,
105- floatSpeaker ,
109+ sideSpeaker ,
106110} : {
107111 description : string
108- floatSpeaker : SingaporeSpeaker | null
112+ sideSpeaker : SingaporeSpeaker | null
109113} ) {
110114 const paragraphs = parseParagraphs ( description )
115+ const splitAt =
116+ sideSpeaker && paragraphs . length >= 2
117+ ? paragraphs . length - 2
118+ : paragraphs . length
119+ const lead = paragraphs . slice ( 0 , splitAt )
120+ const tail = paragraphs . slice ( splitAt )
111121
112122 return (
113123 < div className = "typography-body-lg mt-8 px-2 pb-8 sm:px-3 lg:mt-12 xl:pb-12 [&>p+p]:mt-4 [&_a]:break-words" >
114- { floatSpeaker && (
115- < div
116- className = "hidden xl:float-right xl:ml-6 xl:block xl:w-[340px] xl:pt-[170px]"
117- style = { {
118- shapeOutside : "inset(170px 0 0 0)" ,
119- shapeMargin : "16px" ,
120- } }
121- >
122- < SpeakerCard speaker = { floatSpeaker } index = { 0 } />
124+ { lead . map ( ( html , i ) => (
125+ < p key = { `lead-${ i } ` } dangerouslySetInnerHTML = { { __html : html } } />
126+ ) ) }
127+ { tail . length > 0 && (
128+ < div className = "mt-4 xl:flex xl:items-end xl:gap-6" >
129+ < div className = "[&>p+p]:mt-4 xl:flex-1" >
130+ { tail . map ( ( html , i ) => (
131+ < p key = { `tail-${ i } ` } dangerouslySetInnerHTML = { { __html : html } } />
132+ ) ) }
133+ </ div >
134+ { sideSpeaker && (
135+ < div className = "hidden xl:-mb-12 xl:-mr-3 xl:block xl:w-[580px] xl:shrink-0 xl:[&>article]:border-b-0 xl:[&>article]:border-r-0 xl:[&>article]:border-t" >
136+ < SpeakerCard speaker = { sideSpeaker } index = { 0 } />
137+ </ div >
138+ ) }
123139 </ div >
124140 ) }
125- { paragraphs . map ( ( html , i ) => (
126- < p key = { i } dangerouslySetInnerHTML = { { __html : html } } />
127- ) ) }
128141 </ div >
129142 )
130143}
131144
132145/**
133146 * Split FOST description HTML (a sequence of `<p>...</p>` blocks) into the
134147 * inner HTML of each paragraph so we can render them as real React `<p>`
135- * siblings — needed because `shape-outside` only takes effect when the float
136- * shares a block formatting context with the surrounding text .
148+ * siblings — needed so we can splice the speaker card in alongside the last
149+ * couple of paragraphs at xl+ .
137150 */
138151function parseParagraphs ( html : string ) : string [ ] {
139152 const formatted = formatDescription ( html )
0 commit comments