.author{grid-column:1;grid-row:3;align-content:flex-start}.Metadata_root__oCstk>.description,.Metadata_root__oCstk>.pills-list,.Metadata_root__oCstk>.stats,.Metadata_root__oCstk>.title{grid-column:1/span 2}.Metadata_root__oCstk>.title{margin-bottom:8px}.Metadata_root__oCstk>.stats{margin-bottom:12px}.Metadata_root__oCstk>.description{margin-top:12px}.Metadata_root__oCstk>.pills-list{margin-block-start:20px;margin-block-end:18px}.Metadata_root__oCstk>.actions{display:none}@media screen and (max-width:928px){.Metadata_root__oCstk{margin-block:8px 20px}.Metadata_root__oCstk>.stats{margin-bottom:16px}.Metadata_root__oCstk>.description{margin-top:0}.Metadata_root__oCstk>.author{grid-column:2;grid-row:5;margin-block-start:12px;margin-block-end:6px;justify-content:end}.Metadata_root__oCstk>.actions{display:flex;margin-top:16px;grid-column:1;grid-row:5}.Metadata_root__oCstk>.pills-list{margin-block-start:16px;margin-block-end:0}}.Metadata_root__oCstk .metadata-recs{grid-column:1/-1}.Heading_heading__3MAvZ{color:var(--blue-gray-900)}.Heading_h1__3k7S2{font-size:32px;font-weight:700}.Heading_h2__f9yvs{font-size:28px;font-weight:600}.Heading_h3__f1djd{font-size:24px}.Heading_h4__7tfLE{font-size:20px}.Heading_h5__jVM0l{font-size:16px;font-weight:400}.Heading_h6__uUTrd{font-size:14px;font-weight:400}.Title_root__svkHQ{color:var(--blue-gray-900);font-size:clamp(26px,1vw + 1rem,28px);font-weight:600;line-height:1.25;min-width:0;word-break:break-word}.Stats_root__p_BoZ{flex-wrap:wrap;display:flex;align-items:center;-moz-column-gap:6px;column-gap:6px;color:var(--blue-gray-600);white-space:nowrap;font-size:16px}.Stats_leftContent__588PR,.Stats_rightContent__8d0AF{display:flex;gap:6px}.Stats_root__p_BoZ span{font-size:16px}.Stats_root__p_BoZ .Stats_aiTag__zTzW8{margin-left:10px}@media screen and (max-width:928px){.Stats_root__p_BoZ span{line-height:1.5}.Stats_root__p_BoZ.Stats_extendedMetadata__wb62p .Stats_leftContent__588PR{width:100%}.Stats_root__p_BoZ .Stats_aiTag__zTzW8{margin:8px 0 0}.Stats_root__p_BoZ.Stats_extendedMetadata__wb62p .Stats_formatTypesBullet__xDv0L{display:none}}.Likes_root__WVQ1_{cursor:pointer;transition:color .2s ease-in-out;border-radius:4px}.Likes_root__WVQ1_:hover{color:var(--blue-gray-700)}.Tooltip_root__7FS0Y{background:var(--midnight-green-dark);border-radius:4px;box-shadow:0 .5px 5px rgba(0,0,0,.04),0 4px 11px rgba(0,0,0,.2);color:var(--white);font-weight:400;font-size:12px;line-height:15px;padding:6px 8px;opacity:0;visibility:hidden;animation:Tooltip_show__qVG5k .2s ease-in-out forwards;z-index:var(--popup-index)}.Tooltip_triggerWrapper___S2HG{flex-shrink:0;position:relative;align-items:center;justify-content:center}@keyframes Tooltip_show__qVG5k{to{opacity:1;visibility:visible}}.Tooltip_large__J4Fvl{padding:16px;display:flex;flex-direction:column;background:#fff;color:var(--black)}.AITag_tag__Xx37c{padding:0 12px;height:25px;border-radius:16px;background-color:#f0f2f9;color:#16171b;display:inline-flex;gap:8px;font-size:14px;line-height:1.5;font-weight:600}.AITag_tooltipContent__7JZR_{width:332px}.Author_root___6Bx5{--link-color:var(--blue-gray-800);position:relative;display:flex;align-items:center;gap:8px}.Author_link___lVxw{z-index:1;color:var(--link-color);font-weight:600;display:block}.Author_link___lVxw:before{content:"";position:absolute;inset:0}.Author_follow__Lw4TS{z-index:1}@media screen and (max-width:928px){.Author_link___lVxw:hover{color:var(--blue-gray-800)}}.Avatar_root__GNWHY{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;background-color:var(--white);color:var(--blue-gray-300);border-radius:50%;font-size:16px;font-weight:600;text-align:center;-webkit-user-select:none;-moz-user-select:none;user-select:none;overflow:hidden}.Avatar_initials__EJfVt{color:var(--white);transition:background-color .2s ease-in-out}.Avatar_initials__EJfVt,.Avatar_initials__EJfVt:hover{background-color:var(--blue-gray-600)}.Avatar_image__Bbtll{width:100%;height:100%;-o-object-fit:cover;object-fit:cover}.FollowButton_root__FxpBi{display:inline-flex;background-color:transparent;border:1px solid transparent;border-radius:4px;font-size:12px;padding:1px 6px;transition:background-color .2s ease-in-out,border-color .2s ease-in-out;cursor:pointer}.FollowButton_following__xKCww{border-color:#bf5905;color:#bf5905}.FollowButton_following__xKCww:hover{background-color:#ffead7;border-color:rgba(191,89,5,.5)}.FollowButton_follow__d_6u5{border-color:var(--celadon-blue-dark);color:var(--celadon-blue-dark)}.FollowButton_follow__d_6u5:hover{background-color:#eaf7ff;border-color:rgba(2,126,176,.5)}@media screen and (max-width:928px){.FollowButton_root__FxpBi{display:none}}.Description_root__kt4uq{--line-height:26px;position:relative}.Description_root__kt4uq.Description_clamped__PaV_1{padding-bottom:25px}.Description_root__kt4uq.Description_clamped__PaV_1 .Description_wrapper__hYE9_{mask-image:linear-gradient(to bottom,var(--white),transparent);-webkit-mask-image:linear-gradient(to bottom,var(--white),transparent)}.Description_wrapper__hYE9_{min-height:var(--line-height);display:-webkit-box;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical;-webkit-line-clamp:2}.Description_noClamp__1z7c5,.Description_wrapper__hYE9_.Description_expanded__lRamt{-webkit-line-clamp:unset;-webkit-mask-image:none;mask-image:none}.Description_wrapper__hYE9_.Description_expanded__lRamt{height:auto}.Description_wrapper__hYE9_ p{color:var(--blue-gray-600);font-size:18px;line-height:var(--line-height);white-space:pre-wrap;word-break:break-word}.Description_more__ChrRK{position:absolute;padding:0;bottom:0;height:26px}.Description_less__BvWbY{padding:0}.Description_hidden__a9QZJ{display:none}@media screen and (max-width:928px){.Description_more__ChrRK{right:0;background-color:#fff}.Description_root__kt4uq.Description_clamped__PaV_1{padding-bottom:0}.Description_less__BvWbY,.Description_more__ChrRK{height:var(--line-height)}}.PillsList_root__2EydN{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.Pill_root__IqOYH{--bg:color-mix(in srgb,var(--celadon-blue-dark),90% transparent);--color:var(--blue-gray-800);height:40px;display:inline-flex;align-items:center;gap:6px;background-color:var(--bg);border-radius:100vmax;color:var(--color);font-size:16px;font-weight:600;padding-inline:16px;transition:color .2s ease-in-out,background-color .2s ease-in-out,filter .2s ease-in-out;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;text-decoration:none;white-space:nowrap}.Pill_root__IqOYH:not(.Pill_selected__VPtHm):hover{filter:brightness(.6)}.Pill_root__IqOYH.Pill_selected__VPtHm{--bg:var(--blue-gray-900);--color:var(--white)}@media screen and (max-width:520px){.Pill_root__IqOYH{height:28px;padding-inline:12px;gap:4px;font-size:12px}.Pill_icon__xE_Cg{--size:18px!important}}.Actions_root__00yIC{display:flex;align-items:baseline;gap:6px}.ReadingModeTooltip_root__laf5h{width:277px}.ToggleButtonGroup_root__vtGE_{display:inline-flex;gap:4px;padding:5px 8px;border-radius:4px;background-color:var(--white);border:1px solid var(--blue-gray-200)}.ToggleButton_root__jGx6U{border-radius:4px;background-color:var(--white);color:var(--blue-gray-700);border:none;width:44px;height:34px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}.ToggleButton_selected__51g6d{background-color:var(--blue-gray-200)}.SaveLoggedIn_icon__lk74r{color:var(--blue-gray-700)}.PopoverMenuContent_root__MsRtR{background:var(--white);box-shadow:0 .5px 5px rgba(0,0,0,.04),0 4px 11px rgba(0,0,0,.2);border-radius:4px;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out;z-index:1000;overflow-y:auto}.PopoverMenuContent_root__MsRtR.PopoverMenuContent_visible__O86I_{opacity:1;visibility:visible;transition-delay:0s}.PopoverMenuItem_item__iazpP{width:100%;display:flex;align-items:center;background-color:transparent;color:inherit;cursor:pointer;font-size:inherit;line-height:24px;padding:12px 16px;white-space:nowrap}.PopoverMenuItem_item__iazpP.PopoverMenuItem_highlight__inbqK,.PopoverMenuItem_item__iazpP:hover{background-color:var(--blue-gray-100)}.PopoverMenuSeparator_separator__UpSGw{width:calc(100% - 32px);height:1px;background-color:var(--blue-gray-300);margin:8px 16px}.SavePopover_popover__mZhIY{width:290px;max-height:220px;color:var(--blue-gray-800);font-weight:400;padding:6px 8px;font-size:12px;line-height:15px;overflow:hidden}.SavePopover_saveToNewList__MVBSu{display:flex;align-items:center;gap:6px;color:var(--midnight-green-dark);font-weight:600;font-size:18px;line-height:24px;padding:10px;width:100%}.SavePopover_addIcon__aJLJ3{color:var(--blue-gray-800)}.SavePopover_horizontalSeparator__bsI6Y{border:none;border-bottom:1px solid var(--blue-gray-300);margin:8px 0}.SavePopover_listsContainer__tGGtp{height:100%;max-height:144px;display:flex;flex-direction:column;align-items:center;overflow-y:auto}.SavePopover_saveToList__Fsfl9{color:var(--blue-gray-800);font-weight:400;font-size:16px;justify-content:space-between;line-height:24px;padding:12px 14px 12px 16px;width:100%}.SavePopover_saveToList__Fsfl9 span:not(.SavePopover_clampLines__Blxtm){display:flex;flex-direction:row;gap:35px;justify-content:space-between}.SavePopover_saveToList__Fsfl9 span.SavePopover_clampLines__Blxtm{display:inline-block;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap;width:180px}.SavePopover_listIcon__OVsXv{color:var(--blue-gray-900)}.SavePopover_noSavedLists__0Mh_H{color:var(--blue-gray-600);font-weight:400;font-size:16px;line-height:24px;padding:12px 16px;text-align:center}.SaveOptionsDrawer_drawerTrigger__Gb7nK{box-shadow:none;border:none;border-radius:4px;position:relative;z-index:7}.SaveOptionsDrawer_drawerTrigger__Gb7nK:active,.SaveOptionsDrawer_drawerTrigger__Gb7nK:active:focus,.SaveOptionsDrawer_drawerTrigger__Gb7nK:hover{border:none;box-shadow:none;background-color:transparent}.SaveOptionsDrawer_drawerTrigger__Gb7nK:focus{background:transparent}.SaveOptionsDrawerContent_drawerContent__J5JTL{margin:16px}.SaveOptionsDrawerContent_separator__UW5Rz{margin:8px 0}.SaveOptionsDrawerContent_itemsContainer__tCXw5{display:flex;flex-direction:column;margin-top:0;max-height:144px;overflow-y:auto}.SaveOptionsDrawerContent_drawerActionSecondary__7cq8j{font-weight:400;line-height:24px;padding:0 8px;margin:8px 0;width:100%}.SaveOptionsDrawerContent_drawerActionSecondary__7cq8j span{color:var(--blue-gray-800);justify-content:space-between;gap:30px}.SaveOptionsDrawerContent_drawerActionPrimary____QuR span{gap:14px}.SaveOptionsDrawerContent_drawerActionPrimary____QuR{color:var(--blue-gray-800);display:flex;font-weight:400;line-height:24px;align-items:center;justify-content:flex-start;padding:8px 4px;margin:14px 0}.SaveOptionsDrawerContent_drawerActionPrimary____QuR.SaveOptionsDrawerContent_newListButton__VzhfD{font-size:16px;font-weight:600}.SaveOptionsDrawerContent_addIcon__Wb2cq{color:var(--blue-gray-800)}.SaveOptionsDrawerContent_drawerActionSecondary__7cq8j span.SaveOptionsDrawerContent_clampLines__zfkfI{display:inline-block;overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap;width:calc(100vw - 115px)}.SaveOptionsDrawerContent_listIcon__5dcfC{color:var(--blue-gray-900)}.SaveOptionsDrawerContent_noSavedLists__cpUBY{color:var(--blue-gray-600);font-weight:400;font-size:16px;line-height:24px;padding:12px 16px;text-align:center}.Separator_root__70Ime{--orientationMargin:0;background-color:var(--blue-gray-200);flex-shrink:0}.Separator_horizontal__czVEa{width:calc(100% - var(--orientationMargin) * 2);height:1px}.Separator_vertical__JYCCK{width:1px;height:calc(100% - var(--orientationMargin) * 2)}.SaveLoggedOut_icon__ny9X2{color:var(--blue-gray-700)}.Dropdown_root__Z78h8{display:inline-block;position:relative;color:inherit}.DropdownTrigger_trigger__SzsBj{display:flex;align-items:center;justify-content:center;background:transparent;border:none;font-size:inherit;padding:0;margin:0;cursor:pointer}.DropdownTrigger_trigger__SzsBj:active,.DropdownTrigger_trigger__SzsBj:focus,.DropdownTrigger_trigger__SzsBj:hover{background:transparent}.DropdownContent_content__3daFs{position:absolute;display:flex;flex-direction:column;align-items:flex-start;padding:8px 0;background-color:#fff;box-shadow:0 .5px 5px rgba(0,0,0,.039),0 3.75px 11px rgba(0,0,0,.19);border-radius:4px;color:var(--blue-gray-800);opacity:0;visibility:hidden;transition:transform .15s,opacity .15s,visibility 0s linear .15s;transform:scale(.95);z-index:var(--dropdown-index)}.DropdownContent_bottom-left__gioqM{top:calc(100% + 6px);left:0;transform-origin:top left}.DropdownContent_bottom-right__QJ94h{top:calc(100% + 6px);right:0;transform-origin:top right}.DropdownContent_top-left__O3Ryp{bottom:calc(100% + 6px);left:0;transform-origin:bottom left}.DropdownContent_top-right___Qe45{bottom:calc(100% + 6px);right:0;transform-origin:bottom right}.DropdownContent_content__3daFs.DropdownContent_open__6VoiP{visibility:visible;opacity:1;transform:none;transition-delay:0s}.DropdownItem_item__Sv0GT{width:100%;display:flex;align-items:center;background-color:transparent;color:inherit;cursor:pointer;font-size:inherit;line-height:24px;padding:12px 16px;white-space:nowrap}.DropdownItem_highlight__jO3zg,.DropdownItem_item__Sv0GT:hover{background-color:var(--blue-gray-100)}.DropdownSeparator_separator__EN82n{width:calc(100% - 32px);height:1px;background-color:var(--blue-gray-300);margin:8px 16px}.MoreDropdownButton_moreOptionsDropdown__GK_Uw{display:flex;align-items:center;justify-content:center;width:32px;height:32px}.MoreDropdownButton_item__t4HmI{gap:12px}.MoreDropdownButton_moreOptionsIcon__TpJLA{color:var(--blue-gray-700)}.MoreDropdownButton_icon__DxfY4{color:var(--blue-gray-800)}.MoreDropdownButton_tooltip__az4od{white-space:nowrap}.MetadataToolbar_root__c03ao{--shadow-opacity:0;display:grid;height:var(--metadata-toolbar-height);background-color:var(--white);z-index:var(--header-index)}.MetadataToolbar_wrapper__r7XEc{position:relative;display:grid;grid-template-columns:minmax(0,1fr) max-content 1fr;align-items:center;padding-inline-end:20px;padding-block:16px}.MetadataToolbar_underline__QQn0C{grid-column:1/-1;grid-row:2;height:1px;position:absolute;bottom:0;right:-20px;left:-30vw;background-color:var(--blue-gray-200);box-shadow:0 2px 4px 0 rgba(0 0 0/var(--shadow-opacity));animation:MetadataToolbar_reveal-shadow__5yBxP linear both;animation-timeline:scroll(block);animation-range:150px 450px}.MetadataToolbar_verticalMode__Bh759{position:sticky}.MetadataToolbar_title__jfTWv{font-size:18px;font-weight:600}.MetadataToolbar_actions__FB33C{width:-moz-max-content;width:max-content;justify-self:flex-end;display:flex;grid-column-end:-1}.MetadataToolbar_pageNumber__i6Bhj{display:inline-flex;align-items:center;margin-inline:1em;height:44px}.MetadataToolbar_isInReadingModeVariant__XMDcr{align-items:flex-start}.MetadataToolbar_isInReadingModeVariant__XMDcr .MetadataToolbar_downloadButton__ncS7o>button{padding:0 15px}@media screen and (max-width:928px){.MetadataToolbar_root__c03ao{display:none}}@media screen and (min-width:929px) and (max-width:1249px){.MetadataToolbar_isInReadingModeVariant__XMDcr .MetadataToolbar_pageNumber__i6Bhj{display:none}}@keyframes MetadataToolbar_reveal-shadow__5yBxP{to{--shadow-opacity:0.122}}.DownloadButton_root__adY00{margin-left:auto;display:inline-grid;gap:6px;justify-items:center;flex-shrink:0}.DownloadButton_savedStyling__k18od{font-weight:600;font-size:18px}.DownloadMultipleFormatDrawer_root__CWFxX{width:100%;padding:0 24px}.DownloadMultipleFormatDrawer_drawerHeading__8LnFw{margin:16px 0}.DownloadMultipleFormatDrawer_drawerContent__y815X{width:100%;padding:24px 0}.DownloadMultipleFormatDrawer_drawerRadioButtons__I_lQ4 label{margin-bottom:20px}.DownloadMultipleFormatDrawer_drawerRadioButtons__I_lQ4{margin-bottom:4px}.Fieldset_root__L2NQU{display:grid;padding:0;border:0}.Fieldset_root__L2NQU legend{display:none}.DownloadMultipleFormatPopover_popoverContent__IJudF{min-width:185px}.FadeInOut_root__v7Efq{position:relative;min-width:0;background-color:var(--snow-gray);padding:20px}.FadeInOut_root__v7Efq.FadeInOut_isInfographic__PdX2K{background-color:unset;padding-inline-start:0;padding-inline-end:20px}@media (max-width:928px){.FadeInOut_root__v7Efq,.FadeInOut_root__v7Efq.FadeInOut_isInfographic__PdX2K{background-color:unset;padding-inline:2px}}.FadeInOut_in__9dYWz{animation:FadeInOut_fadeIn__14JD0 .5s}.FadeInOut_out___eDZk{animation:FadeInOut_fadeOut__ILQaD .4s}@keyframes FadeInOut_fadeIn__14JD0{0%{opacity:0}to{opacity:1}}@keyframes FadeInOut_fadeOut__ILQaD{0%{opacity:1}to{opacity:0}}.VerticalSlideOverlayed_root__9Thd4{position:relative}.VerticalSlideOverlayed_root__9Thd4 .vertical-slide-image{position:absolute;inset:0}.VerticalSlideOverlayed_active___p5f2 .vertical-slide-image{opacity:.4}.VerticalSlideOverlayed_active___p5f2{background-color:#000}.VerticalSlideImage_image__VtE4p{width:100%;height:100%;-o-object-fit:contain;object-fit:contain;box-shadow:0 0 0 1px var(--blue-gray-200);border-radius:8px;transition:opacity .3s cubic-bezier(.2,0,0,1);opacity:.9;overflow:clip;background-color:var(--blue-gray-600);animation-name:VerticalSlideImage_pulse__OPBSn;animation-direction:alternate;animation-duration:1s;animation-iteration-count:infinite;animation-timing-function:ease-in-out}.VerticalSlideImage_image__VtE4p.VerticalSlideImage_isLoaded__r4xAa{opacity:1;animation:none;background-color:unset}@keyframes VerticalSlideImage_pulse__OPBSn{0%{opacity:.09}to{opacity:.15}}.SlideScrollDetector_root__AIK38{position:relative;height:100%;width:100%;pointer-events:none}.SlideScrollDetector_detector__rhkyk{position:absolute;top:0;left:0;height:1px;width:100%}.SlideScrollDetector_detector1__NmF8s{top:25%}.SlideScrollDetector_detector2__32XWr{top:75%}.SlideActions_root__fB9Q4{position:absolute;right:0;top:0;display:grid;grid-template-columns:40px;grid-template-rows:40px 40px;grid-column-gap:12px;-moz-column-gap:12px;column-gap:12px;grid-row-gap:12px;row-gap:12px;padding:12px;justify-content:flex-end;align-items:center;text-align:end}.SlideActions_active__aD_e1{grid-template-columns:max-content 40px}.SlideActions_button__o8UXk{background-color:var(--blue-gray-100);color:var(--blue-gray-700)}.VerticalSlide_infographic__ij1FA,.VerticalSlide_root__jU_9r{position:relative}.VerticalSlide_root__jU_9r .vertical-slide-image{position:absolute;inset:0}.VerticalSlide_infographic__ij1FA .vertical-slide-image{position:unset}.VerticalPlayer_root__K8_YS{position:relative;display:grid;grid-template-columns:minmax(0,1fr);grid-gap:24px;gap:24px}@media screen and (max-width:928px){.VerticalPlayer_root__K8_YS{gap:8px}}.FreestarVideoAd_root__KDWgl{min-width:0;flex-shrink:0;aspect-ratio:16/9}.VerticalInterstitialAdWrapper_root__LxQh8{container-type:inline-size;position:relative;display:grid;min-height:280px;overflow-x:clip}.VerticalInterstitialAdWrapper_root__LxQh8:has(.interstitial-ad-container.has-fetched):not(:has(.interstitial-ad-container.has-fetched .freestar-ad-container:not(.unfilled))){display:none}.VerticalInterstitialAdWrapper_root__LxQh8:has(div.interstitial-ad-container.has-fetched div.freestar-ad-container.filled div),.VerticalInterstitialAdWrapper_root__LxQh8:has(div.interstitial-ad-container.has-fetched div.freestar-ad-container.unfilled iframe){display:block!important}.VerticalInterstitial_root__Dunl7{display:none}@media (max-width:520px){.VerticalInterstitial_root__Dunl7:not(.VerticalInterstitial_inVariant__xB9lL){display:block}}@container (max-width: 480px){.VerticalInterstitial_root__Dunl7.VerticalInterstitial_inVariant__xB9lL{display:block}}.FreestarAdContainer_root__qPPC_{position:relative;display:grid;place-content:center}.FreestarAdContainer_root__qPPC_.FreestarAdContainer_withFallback__A4lgm{aspect-ratio:var(--fallback-aspect-ratio)}.FreestarAdContainer_fallback__WreT9{position:absolute;inset:0;grid-template-columns:unset;place-content:center}.AdFallback_root__uAXsl{display:grid;justify-items:center;grid-template-columns:1fr;z-index:0}.MultipleIncontentSmall_root__x58Hs{display:none;grid-template-columns:repeat(auto-fit,minmax(max-content,300px));place-content:center;gap:16px}@media (min-width:521px){.MultipleIncontentSmall_root__x58Hs:not(.MultipleIncontentSmall_inVariant__uf1S8){display:grid}}@container (max-width: 616px){.MultipleIncontentSmall_root__x58Hs:not(.MultipleIncontentSmall_inVariant__uf1S8) .freestar-ad-container:nth-of-type(2){display:none}}@container (min-width: 616px) and (max-width: 688px){.MultipleIncontentSmall_root__x58Hs.MultipleIncontentSmall_inVariant__uf1S8{display:grid}}.MultipleIncontentWide_root__4WD8U{display:none;place-content:center}@container (min-width: 480px) and (max-width: 616px){.MultipleIncontentWide_root__4WD8U{display:grid}}.MultipleIncontentLarge_root__pGIAn{display:none;grid-template-columns:repeat(auto-fit,minmax(max-content,336px));place-content:center;gap:16px}@container (min-width: 688px){.MultipleIncontentLarge_root__pGIAn{display:grid}}.SlideRecs_root__likA5{--card-hover-background:var(--blue-gray-200);display:flex;flex-direction:column;gap:20px}.SlideRecs_root__likA5 h2{font-size:24px;font-weight:600}@media (max-width:1050px){.SlideRecs_root__likA5{display:none}}.SlideRecs_root__likA5 .SlideRecs_cards__Lbxtt{display:grid;grid-gap:16px;gap:16px}.SlideRecs_root__likA5 .SlideRecs_card__txc2D{position:relative;grid-template-columns:180px 1fr;grid-template-rows:auto;gap:16px}.SlideRecs_root__likA5 .slideshow-thumbnail{box-shadow:0 0 0 1px var(--blue-gray-200)}.SlideRecs_root__likA5 .slideshow-card-content{padding-block:0;padding-inline-end:36px;gap:8px}.SlideRecs_root__likA5 .slideshow-title{line-height:1;margin-block-end:0}.SlideRecs_root__likA5 .SlideRecs_wrapper__21j_w{display:flex;gap:8px;align-items:center}.SlideRecs_root__likA5 .SlideRecs_wrapper__21j_w a,.SlideRecs_root__likA5 .SlideRecs_wrapper__21j_w span{font-size:12px}.SlideRecs_root__likA5 .SlideRecs_save__RR8dD{position:absolute;top:8px;right:8px}.SlideRecs_root__likA5 .SlideRecs_author__zlhWO{color:var(--blue-gray-700);font-weight:600;text-decoration:none;z-index:2}.SlideRecs_root__likA5 .SlideRecs_author__zlhWO:first-letter{text-transform:uppercase}.SlideRecs_root__likA5 .SlideRecs_card__txc2D .slideshow-title{font-size:16px}.SlideRecs_root__likA5 .SlideRecs_description__0bGsx{color:var(--blue-gray-700);font-size:14px;display:-webkit-box;overflow:hidden;line-clamp:1;-webkit-line-clamp:1;-webkit-box-orient:vertical}.SlideRecs_root__likA5 .SlideRecs_dot__Db7HR{font-size:16px}.SlideRecs_root__likA5 .SlideRecs_tags__RCA1q{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.SlideRecs_root__likA5 .SlideRecs_tags__RCA1q span{display:grid;place-content:center;height:20px;background-color:var(--alice-blue-600);border-radius:100vmax;color:var(--blue-gray-700);font-size:11px;font-weight:600;padding-inline:12px;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap;text-transform:capitalize;z-index:2}.RecWithPopover_root__iNXfW{width:330px;display:flex;flex-direction:column;gap:8px}.RecWithPopover_root__iNXfW>*{line-height:18px}.RecWithPopover_root__iNXfW h3{font-size:16px;font-weight:600}.RecWithPopover_root__iNXfW p{flex:1 1;color:var(--blue-gray-600);font-size:14px;max-width:100%;display:-webkit-box;line-clamp:5;-webkit-line-clamp:5;-webkit-box-orient:vertical;overflow-y:hidden}.RecWithPopover_hasSiblingsWithPopover__dlaxN{border-radius:4px;padding:12px}.RecWithPopover_hasPopover__G8_rr:hover:not(:has(:is(.save:hover,.author:hover))){background-color:var(--card-hover-background)}.SlideshowCard_root__pD8t4{position:relative;display:grid;grid-template-rows:max-content minmax(0,1fr);grid-template-columns:minmax(0,1fr);align-content:flex-start;color:var(--blue-gray-600)}.SlideshowCard_root__pD8t4:hover .SlideshowCard_thumb__86aJk{scale:1.02}.SlideshowCard_content__xh7kV{display:flex;flex-direction:column;-moz-column-gap:8px;column-gap:8px;padding:16px 0}.SlideshowCardLink_root__p8KI7{position:absolute;inset:0;z-index:1;margin:4px}.Thumbnail_root__qLW0K{--ease:cubic-bezier(0.2,0,0,1);position:relative;background-color:var(--blue-gray-100);border:1px solid var(--blue-gray-100);border-radius:8px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);scale:1;transition:scale .2s var(--ease);overflow:hidden}.Thumbnail_thumb__UXO3a{--reveal-delay:calc(30ms * var(--index));position:absolute;inset:0;width:100%;height:100%;opacity:0;transition:opacity .3s var(--ease);transition-delay:var(--reveal-delay)}.Thumbnail_loaded__XOJ5p{opacity:1}.Thumbnail_blur__opK6A{filter:blur(8px)}.Thumbnail_cover__1zsIi{-o-object-fit:cover;object-fit:cover}.Thumbnail_contain__K6M0d{-o-object-fit:contain;object-fit:contain}.SlideshowTitle_root__2VccW{display:-webkit-box;color:var(--blue-gray-900);font-size:18px;font-weight:600;line-height:1.2;margin-bottom:8px;white-space:break-spaces;word-break:break-word;-webkit-box-orient:vertical}.RecSaveButton_root__0CS9m{grid-area:d}.RecSaveButton_icon__btwCp{fill:currentColor;z-index:2}@media (max-width:928px){.RecSaveButton_root__0CS9m{display:none}}.SlideshowAuthor_root__IkT1_{color:var(--celadon-blue);font-weight:600;text-decoration:underline;-webkit-text-decoration-color:transparent;text-decoration-color:transparent;text-decoration-thickness:1.5px;text-underline-offset:2px;transition:-webkit-text-decoration-color .2s ease-out;transition:text-decoration-color .2s ease-out;transition:text-decoration-color .2s ease-out,-webkit-text-decoration-color .2s ease-out;z-index:2}.SlideshowAuthor_root__IkT1_:hover{-webkit-text-decoration-color:var(--celadon-blue);text-decoration-color:var(--celadon-blue)}.CountTag_root__y1hE1,.SplitDot_root__lTZDc{color:var(--blue-gray-600);font-weight:400}.SlideshowStats_root__EQOR1{display:flex;align-items:center;gap:6px}.SlideshowStats_text___WD7l{color:var(--blue-gray-600)}.BelowReaderAd_root__NKeGg{margin-top:16px;margin-bottom:60px}.BelowReaderAd_root__NKeGg.BelowReaderAd_desktop__7_JN7{display:block;justify-items:flex-start}.BelowReaderAd_root__NKeGg.BelowReaderAd_mobile__08T3d{display:none}.BelowReaderAd_root__NKeGg .fallback-ad{justify-self:flex-start}@media screen and (max-width:928px){.BelowReaderAd_root__NKeGg.BelowReaderAd_desktop__7_JN7{display:none}.BelowReaderAd_root__NKeGg.BelowReaderAd_mobile__08T3d{display:block;justify-items:center}}.Sidebar_root__1BbNu{width:var(--sidebar-size);max-height:100dvh;overflow:clip scroll;position:sticky;top:var(--metadata-toolbar-offset,0);display:flex;flex-direction:column;padding-inline-start:28px;padding-block-start:32px;padding-block-end:430px}.Sidebar_root__1BbNu.Sidebar_withSidebarAds__0w0dT{max-height:unset;overflow:unset;position:static;top:unset;display:grid;grid-template-rows:repeat(var(--slots),1fr);padding-inline:28px}@media (max-width:1050px){.Sidebar_root__1BbNu,.Sidebar_root__1BbNu.Sidebar_withSidebarAds__0w0dT{display:none}}.AboveRecsAd_root__iTmTR{min-height:280px;margin-block-end:32px}.AboveRecsAd_root__iTmTR .freestar-ad-container{place-content:flex-start}@media (max-width:1050px){.AboveRecsAd_root__iTmTR .freestar-ad-container{place-content:center}}.AboveRecsAd_mobileAd__LYgqf{display:none}.AboveRecsAd_desktopAd__ymykj{display:block}@media (max-width:1050px){.AboveRecsAd_mobileAd__LYgqf{display:block}.AboveRecsAd_desktopAd__ymykj{display:none}}.RailRecommendations_root__zqtZQ{display:flex;flex-direction:column;gap:8px;padding-block-end:24px}.RailRecommendations_title__kt1D2{font-size:24px;color:var(--blue-gray-900);font-weight:600;margin-block-end:20px}@media only screen and (min-width:929px){.RailRecommendations_hidden__7Ct2B{display:none}}.RailCard_root__rZUGY{--card-hover-background:var(--blue-gray-100);width:100%;position:relative;border-radius:8px;padding-block:12px;padding-inline:8px}.RailCard_root__rZUGY.slideshow-card{grid-template-columns:160px minmax(0,1fr);grid-template-rows:auto;gap:12px}.RailCard_root__rZUGY .slideshow-card-content{padding:0}.RailCard_actionContainer__eXUHT{display:grid;grid-template-columns:minmax(0,1fr) 28px;grid-column-gap:8px;-moz-column-gap:8px;column-gap:8px}.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-author,.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-stats,.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-title{grid-area:unset}.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-author,.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-stats{grid-column:1/-1}.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-author{grid-row:2}.RailCard_root__rZUGY .RailCard_actionContainer__eXUHT .slideshow-stats{grid-row:3}.RailCard_link__d3BBm{z-index:1}.RailCard_info__Oqm6G{min-width:0;display:flex;flex-direction:column;justify-content:center;word-break:break-word}.RailCard_root__rZUGY .RailCard_title__Tvfiv{font-size:16px;margin-bottom:0;grid-area:a;word-break:break-word}.RailCard_root__rZUGY .RailCard_stats__ZvZms{margin-top:12px}.RailCard_stats__ZvZms .text{font-size:12px}.RailCard_root__rZUGY .RailCard_author__JYeYZ{color:var(--blue-gray-700);margin-top:8px;text-decoration:none}@media screen and (min-width:929px){.RailCard_root__rZUGY:hover{background-color:var(--blue-gray-100)}}.RelatedContent_root__29Np1{background-color:var(--blue-gray-100);border-top:1px solid var(--blue-gray-200);border-bottom:1px solid var(--blue-gray-200);padding-block:32px;position:relative}.RelatedContent_wrapper__riU7l{display:grid;grid-template-columns:minmax(0,1fr);grid-gap:32px;gap:32px;max-width:var(--max-content-width);margin-inline:auto}.RelatedContent_title__QUhpL{text-align:center;font-size:32px;font-weight:700}.RelatedContent_root__29Np1 .bottom-recs{display:grid}.RelatedContent_root__29Np1 .rail-recs{display:none}@media screen and (max-width:520px){.RelatedContent_root__29Np1 .bottom-recs{display:none}.RelatedContent_root__29Np1 .rail-recs{display:flex}.RelatedContent_wrapper__riU7l{padding-inline:16px}}.BottomRecommendation_root__7aU9w{display:grid;grid-gap:4px;gap:4px;padding-inline:24px}.BottomRecommendation_title__SRj68{font-size:22px;font-weight:600}.BottomRecommendation_count__4HpLo{color:var(--blue-gray-600);font-size:16px;font-weight:400}.Slider_root__c0Jo8{position:relative;display:grid}.Slider_scroller__KHjw4{display:flex;gap:20px;overflow:auto;scroll-snap-type:x mandatory;overscroll-behavior-x:contain;max-inline-size:100%;min-block-size:100%;touch-action:pan-x;-ms-overflow-style:none;scrollbar-width:none}.Slider_scroller__KHjw4::-webkit-scrollbar{display:none}.Slider_scroller__KHjw4>*{flex-grow:1;flex-shrink:0;scroll-snap-align:start}.Slider_scroller__KHjw4>:last-child{scroll-snap-align:end}.Slider_arrow__8LCca{display:grid;place-content:center;width:36px;height:36px;position:absolute;top:50%;background:var(--white);border:1px solid var(--blue-gray-200);border-radius:100vmax;box-shadow:0 2px 4px 0 rgba(0,0,0,.25);color:var(--blue-gray-800);padding:0;opacity:1;visibility:visible;pointer-events:all;transition:opacity .2s ease-in-out,visibility .2s ease-in-out;transition-delay:0s;cursor:pointer;z-index:1}.Slider_prev__YMssa{left:0;translate:-50% -50%}.Slider_next__fa9IO{right:0;translate:50% -50%}.Slider_hidden__rs7nK{opacity:0;visibility:hidden;pointer-events:none}.BottomRecommendationCard_root__gffTk{inline-size:clamp(220px,12.63rem + 3.45vw,260px);position:relative;border-radius:8px;background-color:#fff;border:1px solid var(--blue-gray-200)}.BottomRecommendationCard_root__gffTk .slideshow-thumbnail{border-radius:0;border-top-left-radius:8px;border-top-right-radius:8px}.BottomRecommendationCard_root__gffTk .slideshow-card-content{padding:16px}.BottomRecommendationCard_root__gffTk .slideshow-author{max-width:50%;word-break:break-all;white-space:break-spaces;-webkit-line-clamp:1;display:-webkit-box;-webkit-box-orient:vertical}.BottomRecommendationCard_root__gffTk .slideshow-stats{margin-block-start:auto}.BottomRecommendationCard_root__gffTk:focus,.BottomRecommendationCard_root__gffTk:focus-visible{outline:none;border-color:var(--celadon-blue);border-width:1px}.BottomRecommendationCard_root__gffTk .BottomRecommendationCard_text__5jKNE{display:-webkit-box;-webkit-box-orient:vertical;white-space:break-spaces;word-break:break-word;-webkit-line-clamp:1}.BottomRecommendationCard_link__pHORq:before{content:"";position:absolute;inset:0;z-index:1}.BottomRecommendationCard_metaLine__shwPk{display:flex;gap:8px;align-items:center;white-space:nowrap;grid-area:c}.ScribdRecommendation_root__t3ezS{display:grid;grid-gap:16px;gap:16px;padding:0 24px}.ScribdRecommendation_header__Jw_M1{display:grid;grid-template-columns:1fr max-content}.ScribdRecommendation_title__JZ5p7{font-size:22px;font-weight:600}.ScribdRecommendation_link__4DVQz{align-self:end;grid-column:2;grid-row:1/span 2;color:var(--celadon-blue-dark)}.ScribdRecommendation_link__4DVQz:hover{color:var(--celadon-blue)}.ScribdRecommendationCard_root__ef2Y_{--ease:cubic-bezier(0.2,0,0,1);--rec-bg-1:#f1e3e3;--rec-bg-2:#f6f4e3;--rec-bg-3:#e1eaec;--rec-bg-4:#efebef;--rec-bg-5:#f1f0f0;--rec-bg-6:#f4eadb;--rec-bg-7:#eaeee7;--rec-bg-8:#e3e8ef;--rec-bg-9:#f1eee6;max-width:172px;position:relative;height:100%;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:max-content minmax(0,1fr);align-content:flex-start;background-color:var(--white);border:1px solid var(--blue-gray-200);box-shadow:none;transition:box-shadow .2s ease-in-out;text-decoration:none}.ScribdRecommendationCard_root__ef2Y_:hover{box-shadow:0 2px 10px rgba(0,0,0,.1)}.ScribdRecommendationCard_thumb__5VVNh{position:relative;display:flex;background-color:var(--blue-gray-200);margin-block-start:24px;margin-inline:20px}.ScribdRecommendationCard_root__ef2Y_:before{position:absolute;inset:0;aspect-ratio:19/16;content:"";background-color:var(--block-color)}.ScribdRecommendationCard_thumb__5VVNh img{--reveal-delay:calc(30ms * var(--card-index));aspect-ratio:inherit;box-shadow:0 4px 6px rgba(0,0,0,.2);-o-object-fit:cover;object-fit:cover;opacity:0;visibility:hidden;transition:opacity .3s var(--ease);transition-delay:var(--reveal-delay)}.ScribdRecommendationCard_loaded__FTN_f img{opacity:1;visibility:visible}.ScribdRecommendationCard_content__ObcvL{display:flex;flex-direction:column;padding-block:20px;padding-inline:16px}.ScribdRecommendationCard_content__ObcvL .rating{padding-block-start:8px;margin-block-start:auto}@media (max-width:928px){.ScribdRecommendationCard_content__ObcvL .rating{flex-direction:column;align-items:flex-start}}.Rating_root__fgZQJ{display:flex;align-items:center;-moz-column-gap:6px;column-gap:6px;white-space:nowrap}.Rating_root__fgZQJ :first-child{color:#e47b01;font-size:16px;line-height:1}.Rating_root__fgZQJ :last-child{color:var(--blue-gray-600);font-size:14px;letter-spacing:-.25px}.Transcript_root__Vrf6Q{width:100%;max-width:var(--max-content-width);display:grid;grid-gap:8px;gap:8px;padding:32px 16px;margin-inline:auto;position:relative;background:#fff}.Transcript_title__YgAka{display:flex;align-items:center;gap:4px;font-weight:300;word-break:break-word}.Transcript_list__faItj{list-style-type:none;padding-inline-start:0;word-break:break-word}.Transcript_link__MLbGS{color:var(--celadon-blue);font-weight:700;line-height:22px;text-decoration:none;cursor:pointer}.EditorsNotes_root__3PcDF{padding:32px 16px;margin:0 auto}.EditorsNotes_heading__XR9E6{font-weight:700;font-size:22px}.EditorsNotes_list__NcG5Y{padding-left:30px;font-size:18px;font-style:italic;color:var(--blue-gray-600)}.EditorsNotes_item__ebBbj{word-break:break-word}@media screen and (min-width:1696px){.EditorsNotes_root__3PcDF{max-width:1688px}}.FixedDownloadButton_root__14xtQ{display:none}@media screen and (max-width:928px){.FixedDownloadButton_root__14xtQ{position:sticky;bottom:0;display:flex;justify-content:right;z-index:3;padding:16px}}.Modal_root__TYkzh[open]{opacity:1;animation:Modal_slide-in__GHXut .3s ease-out}.Modal_root__TYkzh{--max-height:calc(100dvb - var(--header-height));--slide-from:calc(-50% + 8px);--slide-to:-50%;--title-size:80px;max-width:100%;max-height:var(--max-height);top:50%;left:50%;translate:-50% -50%;box-shadow:0 0 0 1px rgba(9,30,66,.08),0 2px 1px rgba(9,30,66,.08),0 0 20px -6px rgba(9,30,66,.3);border:0;border-radius:var(--border-radius);padding:0;opacity:0;animation:Modal_slide-out__m_Ov2 .2s ease-in;transition:display allow-discrete .3s,overlay allow-discrete .3s;overflow:clip}@starting-style{.Modal_root__TYkzh[open]{opacity:0}}.Modal_root__TYkzh.Modal_small__hupRE{width:400px}.Modal_root__TYkzh.Modal_medium__j8NOV{width:600px}.Modal_root__TYkzh.Modal_large__ygVmr{width:800px}.Modal_root__TYkzh.Modal_xlarge__HeXWk{width:960px}.Modal_wrapper__4UTGq{position:relative;display:flex;flex-direction:column}.Modal_wrapper__4UTGq>h1+*{flex:1 1;max-height:calc(var(--max-height) - var(--title-size));overflow:clip auto}@media screen and (max-width:520px){.Modal_root__TYkzh,.Modal_root__TYkzh.Modal_large__ygVmr,.Modal_root__TYkzh.Modal_medium__j8NOV,.Modal_root__TYkzh.Modal_small__hupRE,.Modal_root__TYkzh.Modal_xlarge__HeXWk{width:100vw}}@media screen and (max-width:928px){.Modal_root__TYkzh.Modal_bottomPlacement__BUbfp{--slide-from:8px;--slide-to:0;width:100vw;top:unset;bottom:0;translate:-50% 0;border-bottom-left-radius:0;border-bottom-right-radius:0}}@keyframes Modal_slide-in__GHXut{0%{translate:-50% var(--slide-from);opacity:0}to{translate:-50% var(--slide-to);opacity:1}}@keyframes Modal_slide-out__m_Ov2{0%{translate:-50% var(--slide-to);opacity:1}to{translate:-50% var(--slide-from);opacity:0}}.Modal_root__TYkzh::backdrop{background-color:transparent;transition:display allow-discrete .3s,overlay allow-discrete .3s,background-color .3s}.Modal_root__TYkzh[open]::backdrop{background-color:rgba(0,0,0,.6)}@starting-style{.Modal_root__TYkzh[open]::backdrop{background-color:transparent}}.Modal_title__xhSfl{height:var(--title-size);display:flex;align-items:center;color:var(--blue-gray-900);font-size:20px;font-weight:600;border-bottom:1px solid var(--blue-gray-200);padding-inline-start:20px;padding-inline-end:60px}.Modal_title__xhSfl:first-letter{text-transform:capitalize}.Modal_content__R1F4d{padding-inline:20px;padding-block:24px}.Modal_actions__t63hZ{display:flex;align-items:center;justify-content:flex-end;gap:24px;padding-inline:20px;padding-block:16px}.CloseButton_root__JCTRm{position:absolute;right:16px;top:16px;width:40px;height:40px;display:grid;place-content:center;color:var(--blue-gray-600);border-radius:100vmax;background-color:transparent;border:0;padding:0;margin:0;transition:background-color .2s ease-in-out;cursor:pointer}.CloseButton_root__JCTRm:hover{background-color:rgba(var(--blue-gray-600-rgb),.05)}.ReportForm_root__RNqAc{display:grid;grid-gap:10px;gap:10px}.ReportForm_selectField__Kyyaj{max-width:unset!important}.LikeModal_more__R9uAk{justify-self:center;color:var(--celadon-blue);font-weight:500;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out}.LikeModal_more__R9uAk.LikeModal_visible__t1vr4{opacity:1;visibility:visible;transition-delay:0s}.LikesUserList_root__RMFUk{list-style:none;padding:0 0 16px;margin:0}.LikesUserList_root__RMFUk>li{display:grid}.LikesUserList_root__RMFUk>li:last-child .LikesUserList_link__NeMA0{border-bottom:none}.LikesUserList_link__NeMA0{display:grid;grid-template-columns:repeat(2,max-content) 1fr;grid-template-rows:repeat(2,min-content);grid-template-areas:"avatar username summary" "avatar title title";grid-column-gap:12px;-moz-column-gap:12px;column-gap:12px;grid-row-gap:0;row-gap:0;border-bottom:1px solid var(--blue-gray-200);padding:8px 0;text-decoration:none}.LikesUserList_avatar__VRXz2{grid-area:avatar}.LikesUserList_username__c84om{grid-area:username}.LikesUserList_summary___gbSG{grid-area:summary}.LikesUserList_title__UF0V6{grid-area:title}.ViewModal_content__GPKXy{padding-block:0}.ViewModal_row__xLxnz{display:flex;justify-content:space-between;border-bottom:1px dashed var(--blue-gray-300);color:var(--blue-gray-800);padding:16px 0}.ViewModal_row__xLxnz:last-child{border-bottom:none}.ConfirmRemoveSavedModal_description__2EAEu{color:var(--blue-gray-800);font-weight:400;font-size:16px;line-height:20px;padding:24px 0}div.SaveToNewListModal_input__Fi90k{max-width:unset;margin-bottom:24px}.SaveToNewListModal_checkboxWrapper__y_w0m .SaveToNewListModal_checkboxLabel__lMiU9:hover,.SaveToNewListModal_checkboxWrapper__y_w0m:hover .SaveToNewListModal_checkboxLabel__lMiU9,.SaveToNewListModal_checkboxWrapper__y_w0m:hover .SaveToNewListModal_input__Fi90k{color:var(--blue-gray-800)}.SaveToNewListModal_checkboxWrapper__y_w0m .SaveToNewListModal_checkboxLabel__lMiU9{color:var(--blue-gray-800);font-weight:400;font-size:14px;flex-direction:column;display:flex;line-height:18px}.SaveToNewListModal_imageContainer__Dx4nD{display:flex;flex-direction:column;justify-content:center;align-items:center;width:178px;margin:0 auto 24px}.SaveToNewListModal_imageContainer__Dx4nD img{height:100px}.SaveToNewListModal_errorContainer__FBZPH{margin:16px 0}
Svoboda | Graniru | BBC Russia | Golosameriki | Facebook
SlideShare a Scribd company logo
Why Apache Kafka
Clusters Are Like
Galaxies
And Other Cosmic Kafka Quandaries
Explored
Paul Brebner
Instaclustr Technology Evangelist
June 5 2024
© 2024 NetApp, Inc. All rights reserved.
Performance Engineering Track on again at
Community over Code 7-10 October Denver 2024
© 2024 NetApp, Inc. All rights reserved.
© 2024 NetApp, Inc. All rights reserved.
3
Instaclustr Managed Platform
• Cloud Platform for Big
Data Open Source
Technologies
• Free 30 day trial
• Focus of this talk is on
Apache Kafka®
Centenary of Franz Kafka’s death - June 3 2024
© 2024 NetApp, Inc. All rights reserved.
4
Head of Kafka, Prague (Paul Brebner)
Overview
1 Kafka Scalability
2 Kafka Clusters and Zipf’s Law
3 Kafka Clusters and Storage
4 Top 10 Kafka Clusters and Performance
Thanks to Instaclustr colleagues for Kafka cluster data:
Kafka Clusters & Storage - Alastair Daivis & Kafka Team
Top 10 Clusters - Joseph Clay & Ramana Selvaratnam (Technical Operations Team)
A note on Kafka cluster metrics
Easy Performance Metrics Harder
Broker Cluster All Clusters
Size Metrics available
Focus of our metrics
collection is
Per broker
Not per cluster or all clusters
DALL·E 3
Part 1 Kafka Scalability
(Source: Shutterstock)
Kafka is a distributed streams processing
system—it allows distributed producers to send
messages to distributed consumers via a Kafka
cluster.
What is
Kafka?
Cluster = Brokers + Partitions
Enabling Write & Read Concurrency
Partition n
Topic
Partition 1
Producer
Partition 2
Consumer Group
Consumer
Consumer
Consumers share
work within groups
Consumer
Partitions enable Consumers to share work
(c.f. Amish Barn raising) within a consumer group
Multiple groups enable message broadcasting.
Messages are duplicated (c.f. clones) across groups, as each
consumer group receives a copy of each message.
Multiple Groups Enable Message Broadcasting
Consumer
Consumer
Consumer
Consumer
Topic
Partition 1
Partition 2
Partition n
Producer
Consumer Group
Consumer Group
Messages are
duplicated across
Consumer groups
Messages are duplicated (c.f. clones) across groups,
as each consumer group receives a copy of each message
Partitions – concurrency mechanism –
more is better – until it’s not
You need sufficient partitions to benefit from the cluster concurrency
And not too many that the replication overhead impacts overall throughput
0
0.5
1
1.5
2
2.5
1 10 100 1000 10000
Partitions vs. Throughput (M TPS)
ZK TPS (M) KRAFT TPS (M) 2020 TPS (M)
2022 - Better
2020 - Worse
2022 results better due to improvements to Kafka and h/w
© 2024 NetApp, Inc. All rights reserved.
17
• Horizontal Scalability (Brokers/Nodes)
• Vertical Scalability (more/faster cores per Broker)
• Hardware (cores, CPU speed/type, RAM, disk, network, etc)
• Partitions + Consumers
• Optimise number of Partitions
• Consumer speed optimization (slow consumers are bad – high latency and too many partitions)
• Kafka cluster and client configurations (many and complex)
• Goals are typically
• High Throughput
• Fast Latency (low 10s ms)
Kafka Scalability and Performance Summary
(Slow consumers are a problem: Getty)
Part 2 Kafka Clusters and Zipf’s Law – size
distribution
Visual size comparison of the six largest Local Group galaxies, with details (Wikipedia)
© 2024 NetApp, Inc. All rights reserved.
19
• Distribution function
• Most frequent observation is twice as common
• as next and so on (i.e. 1/rank)
• Long-tailed distribution
• 80/20 rule (20% of people own 80% of $)
• C.f. Pareto (discrete vs. continuous)
• Log-log rank vs frequency/size gives approx. straight line
• Common examples
• Frequency of words
• Wealth distribution
• Animal species size
• Earthquakes
• City sizes
• Computer systems (e.g. workload modelling, subsystem capacity)
• Galaxy sizes
Scaling/power law
Zipf’s Law
© 2024 NetApp, Inc. All rights reserved.
20
• Question: How large are the largest
structures in the universe?
• Answer: Bigger!
• Zipf’s law predicted that
• bigger galaxies would be detected in older parts of
the universe
• beyond the reach of the Hubble at the time
• confirmed with the James Webb telescope
observations
• But what’s this got to do with Kafka?
Size and Scale Predictions
Apache Kafka + Galaxies?
Image from NASA’s James Webb Space Telescope showing older and bigger galaxy clusters
© 2024 NetApp, Inc. All rights reserved.
21
Raw Kafka Cluster Size Data - Summary Statistics
3 3 3
4.520702635
7.023373433
96
797
3603
1
10
100
1000
10000
Nodes/Cluster
Summary Statistics (log nodes/cluster)
min median mode average stdev max count sum
© 2024 NetApp, Inc. All rights reserved.
22
Histogram (size vs count) – skewed distribution
Raw Kafka Cluster Size Data
0
100
200
300
400
500
600
700
800
Total
3 4 6 8 9 12 15 18 21 24 27 30 33 36 39 48 60 72 78 96
© 2024 NetApp, Inc. All rights reserved.
23
What is the distribution? Definitely a long-tailed power law
Kafka Clusters and Zipf’s Law
0
20
40
60
80
100
120
0 100 200 300 400 500 600 700 800 900
Size
Cluster
Cluster Size Distribution (largest to smallest)
© 2024 NetApp, Inc. All rights reserved.
24
Approximately Zipfian
Kafka Clusters – log size vs log rank
1
10
100
1000
1 10 100
Log
rank
Log size
Kafka Clusters - Log size vs log rank
© 2024 NetApp, Inc. All rights reserved.
25
Can expect larger clusters (animals, galaxies etc)
So What? Kafka and Zipf’s Law (1)
African Elephant, 7 t
Maraapunisaurus, extinct dinosaur, 150 t
© 2024 NetApp, Inc. All rights reserved.
26
Extrapolation of size from Zipf’s law + largest observed cluster
Predicted larger clusters
0.1
1
10
100
1000
1 10 100 1000
Log
rank
Log size
Kafka Clusters - Log size vs log rank
Rank Predicted larger clusters
Predicted larger clusters
Larger
© 2024 NetApp, Inc. All rights reserved.
27
Estimate total nodes for more clusters
Animal transportation problem
So What? Kafka and Zipf’s Law (2)
How many animals can fit in a boat? Public Domain
© 2024 NetApp, Inc. All rights reserved.
28
Total weight of animals on Ark (assuming Elephant is the largest) tends to 90 tonnes
If you know the size of the biggest thing you can predict the total size
© 2024 NetApp, Inc. All rights reserved.
29
Only increases total nodes by 25%
Doubling number of Kafka clusters
0
500
1000
1500
2000
2500
3000
3500
4000
4500
5000
0 200 400 600 800 1000 1200 1400 1600 1800
Cumulative total nodes
100% more clusters
25%
more
nodes
Part 3 Kafka cluster storage
DALL·E 3
Storage for all Kafka clusters
Available from a recent project
© 2024 NetApp, Inc. All rights reserved.
31
Correlation coefficient between size and disk = 0.9
5.6 PB Total Disk across all Kafka clusters
Raw Data – total disk per cluster
0
50
100
150
200
250
300
350
400
450
500
0 20 40 60 80 100 120
Disk
(TB)
Nodes/cluster
Disk (TB) per cluster
© 2024 NetApp, Inc. All rights reserved.
32
• Disk space used is a function of average write rate x average message size x retention period x RF (Little’s
Law)
• Our metrics our total disk available, not used
• Some clusters are DEV not PROD – not real workloads, and RF may be < 3
• Approximation - number of nodes as a proxy for cluster size – actual instance sizes impact capacity
• Kafka log retention policy and time impact how many messages are retained
• Kafka clusters are sized for peak load not average load
• Some clusters may be older than others (disk can be increased)
• Write vs. Read workload imbalance
• Some clusters may have higher write workload rate (requiring more disk) vs.
• Higher read workload rates (requiring less disk)
What’s going on?
0
100
200
300
400
500
0 20 40 60 80 100 120
Disk
(TB)
Nodes/cluster
Disk (TB) per cluster
Part 4
Performance Metrics for
Top Ten Kafka Clusters
Top 10 tallest buildings (Wikipedia)
But in reality more people are killed by horses, cows, dogs,
and bees than kangaroos, sharks, snakes, crocodiles,
emus, jellyfish, etc!
Most Dangerous
Australian Critters?
Ranking can be tricky
Most “dangerous” = most teeth? Most venomous?
(Paul Brebner) (Wikimedia)
© 2024 NetApp, Inc. All rights reserved.
36
• For all clusters
• Size (number of nodes) and type
• Disk (from extra project)
• Performance Metrics are collected for all clusters
• But not easily available as the focus is per-cluster operations
• Requested Performance Metrics for Top Ten Clusters
• What did I get?
• Static (per cluster):
• Nodes, Topics, Partitions
• For 24 hours (per broker):
• Resource Utilisation: CPU (avg, max)
• Throughput: Bytes in (avg, max), Bytes out (avg, max), Messages in (avg, max) [Have to scale by number of nodes to get cluster metrics]
• Performance: Producer and consumer latency (avg, p99)
What metrics are available for Kafka clusters?
Broker metrics need scaling to cluster metrics
Variation in broker metric values
24 hour sampling loses accuracy
24 hour sample size is limited/biased
Real workloads not benchmarking
Ten biggest clusters by node count only
Speculative Results!
Warning!
© 2024 NetApp, Inc. All rights reserved.
37
Min, Avg, Max
Summary Statistics: Nodes, Topics, Partitions
27
7
2598
56.4
429.7
92145.3
96
1755
508800
1
10
100
1000
10000
100000
1000000
Nodes Topics Partitions
Nodes, Topics, Partitions (Log)
Min Avg Max
© 2024 NetApp, Inc. All rights reserved.
38
Summary Statistics: CPU, GB/s in/out, Message/s (in)
2
0.396
0.12
24.5
3.14175
1.419
67.5
14.4
8.4
0.1
1
10
100
CPU Bytes in/out (GB/s) Messages in (M/s)
CPU, GB/s (in+out), Messages/s (in, M/s) (Log)
Min Avg Max
© 2024 NetApp, Inc. All rights reserved.
39
Producers faster than Consumers
Note that some clusters use EBS, others use SSDs (faster!)
Summary Statistics: Latency (ms)
0.075
6.5
3.2925
106.65
90
700
0.01
0.1
1
10
100
1000
Producer latency (ms) Consumer latency (ms)
Latency (Log)
Min Avg Max
© 2024 NetApp, Inc. All rights reserved.
40
50% of clusters have sub 50ms average latency
Consumer latency distribution
0
50
100
150
200
250
300
350
1 2 3 4 5 6 7 8 9 10
Latency distribution (ms) – increasing
© 2024 NetApp, Inc. All rights reserved.
41
150-3k Bytes
Summary Statistics: Message size (Bytes)
150
1163.950072
3000
0
500
1000
1500
2000
2500
3000
3500
Message size (avg, Bytes)
Message size (avg, Bytes)
min avg max
© 2024 NetApp, Inc. All rights reserved.
42
0.4 to 25 Million/s
Using Average message size, compute messages out à total messages in+out
0
5
10
15
20
25
30
Msgs in+out (M/s)
Msgs in+out (M/s)
min avg max
© 2024 NetApp, Inc. All rights reserved.
43
1.4 to 28 – i.e. 28 consumer groups potentially
Fan out (ratio of consumer to producer messages)
0
5
10
15
20
25
30
Fan out
Fan out
min avg max
© 2024 NetApp, Inc. All rights reserved.
44
Knowing metrics for top 10 clusters we can estimate total values for ALL CLUSTERS
27K topics (probably underestimate), 5.8 M partitions; 321-564 Million messages/s
Assuming Zipf distribution…
27.45051596
5.886516239
321.3248554
564.9712845
1
10
100
1000
1
Grand Totals for All Kafka Clusters
Topics (k) Partitions (M) Msgs in+out (avg, M/s) Msgs in+out (max, M/s)
© 2024 NetApp, Inc. All rights reserved.
45
Nodes – 27 to 96 (1% of clusters, 564 nodes total, 16% of total nodes overall)
Static data – top 10 clusters (largest on right)
27
36 36
48
51
60 60
72
78
96
0
20
40
60
80
100
120
1 2 3 4 5 6 7 8 9 10
Nodes/Cluster
© 2024 NetApp, Inc. All rights reserved.
46
Ranges, odd ones out
Biggest (10) cluster has most partitions; cluster 6 has “hottest” topics (max partitions/topic)
Topics/Partitions/Nodes
7
631
13
1337
57 7 27 101
362
1755
0
500
1000
1500
2000
1 2 3 4 5 6 7 8 9 10
Topics/Cluster
6675
57672
2598
200490
11940 11394 13038 23046
85800
508800
0
100000
200000
300000
400000
500000
600000
1 2 3 4 5 6 7 8 9 10
Partitions/Cluster
0
200
400
600
800
1000
1200
1400
1600
1800
1 2 3 4 5 6 7 8 9 10
Partitions/Topic
0
200
400
600
800
1000
1200
1400
1600
1800
2000
1 2 3 4 5 6 7 8 9 10
Partitions/Node
Most topics Most partitions
Hottest topics
© 2024 NetApp, Inc. All rights reserved.
47
Cluster 4 has highest max = highest topics/partitions per cluster/node
Cluster 6 has highest average = highest partitions/topic (“hot” topics)
These are both ”hot” clusters
CPU
0%
10%
20%
30%
40%
50%
60%
70%
80%
1 2 3 4 5 6 7 8 9 10
CPU (Avg, max)
CPU (Avg) CPU (max)
Hottest
Hot
© 2024 NetApp, Inc. All rights reserved.
48
Topics? Theory and our Technical operations people say probably not
as topics are not correlated to throughput (or size)
Correlation = 0.4, some known smaller clusters with way more topics (e.g. 10,000!)
Any obvious correlations to cluster size?
0
200
400
600
800
1000
1200
1400
1600
1800
2000
0 20 40 60 80 100 120
Total topics in cluster
© 2024 NetApp, Inc. All rights reserved.
49
Partitions are related to throughput and size in theory
Correlation = 0.63, and the largest cluster has most and above average partitions/nodes
Size/Partition correlation?
0
100000
200000
300000
400000
500000
600000
0 20 40 60 80 100 120
Total partitions
© 2024 NetApp, Inc. All rights reserved.
50
Average – poor correlation
Size/Throughput?
0
5000000
10000000
15000000
20000000
25000000
0 20 40 60 80 100 120
Msgs in+out (avg/s)
© 2024 NetApp, Inc. All rights reserved.
51
Max – poor correlation
But avg & peak TP correlates with “hot” cluster
Real workloads in 24 hour sample period don’t necessarily correlate with cluster capacities
Size/Throughput?
0
5000000
10000000
15000000
20000000
25000000
30000000
0 20 40 60 80 100 120
Msgs in+out (max/s)
© 2024 NetApp, Inc. All rights reserved.
52
• AWS ARM Graviton2 R6g high price performance for memory-intensive workloads
• R6g.4xlarge 16 core (EBS) (4 clusters)
• R6g.2xlarge 8 cores (EBS) (2 clusters)
• AWS ARM Graviton2 Im4gn Nitro SSD for I/O intensive workloads
• Im4gn.4xlarge 16 core SSD (2 clusters, including “hot” cluster)
• AWS ARM Graviton2 M6g for balanced workloads
• M6gd.4xlarge 16 cores SSD (1 cluster)
• AWS x86 I3en for data-intensive workloads
• I3en.3xlarge 12 cores SSD (1 cluster)
A mix of EC2 instance types/sizes (4/5) and storage - EBS (6)/SSD (4)
Top 10 clusters have heterogeneous h/w
© 2024 NetApp, Inc. All rights reserved.
53
Good correlation (0.8) – definite increase in total cores for bigger clusters
Cores per Cluster
0
200
400
600
800
1000
1200
1400
1600
1800
0 20 40 60 80 100 120
Cores
per
cluster
Nodes per cluster
Cores per cluster
© 2024 NetApp, Inc. All rights reserved.
54
• Insights from our Techops team – thanks!
• Biggest cluster (#10)
• Over provisioned, 96 nodes, 1536 cores
• EBS (slow)
• Peak in messages/s = 1M/s
• Consumer latency 200 - 400ms
• Runs “cool” (18-45%)
• Most partitions (0.5088 Million)
• Hottest cluster (#6)
• 60 nodes, 960 cores
• Runs “hot” (45-55%)
• But lowest consumer latency
• Faster SSDs
• Few topics, most partitions/topic (hot “topics”)
Drill down
Biggest cluster vs “hottest” cluster
© 2024 NetApp, Inc. All rights reserved.
55
Average for cluster = 290 ms but actually a large variation across brokers
Also illustrates that metrics are per broker – and have wide variability
© 2024 NetApp, Inc. All rights reserved.
56
For target throughput how many cores and partitions are needed (in practice need both)?
Can only predict a range from this data (avg=conservative; max=optimistic)
Capacity Planning
6288.039891
431.386635
25583.88158
2155.566642
0
5000
10000
15000
20000
25000
30000
Msgs/s per core Msgs/s per partition
Msgs/s per core and partition
Avg Max
© 2024 NetApp, Inc. All rights reserved.
57
Range: Avg (conservative), Max (optimistic)
Cores for target throughput (x2 max current cluster)
0
1000
2000
3000
4000
5000
6000
7000
8000
9000
0 10 20 30 40 50 60
TPS (Million/s) vs Cores
Cores (avg) Cores (max)
© 2024 NetApp, Inc. All rights reserved.
58
Range: Avg (conservative) max (optimistic)
Note: This is probably skewed due to large cluster with most partitions having low throughput
and “hot” cluster with highest throughput having few partitions!
Partitions for target throughput (x2 max current cluster)
0
20000
40000
60000
80000
100000
120000
0 10 20 30 40 50 60
TPS (Million/s) vs Partitions
Partitions (avg) Partitions (max)
© 2024 NetApp, Inc. All rights reserved.
59
• Lots of small clusters
• Few big clusters
• Even bigger clusters are likely
• A wide distribution of sizes is observed
• Kafka is horizontally scalable
• Fits many different customer workloads
• Some customers have many smaller clusters
• Some clusters grow in size over time
Conclusions?
Kafka cluster size distribution is Zipfian
DALL·E 3
© 2024 NetApp, Inc. All rights reserved.
61
• Wide range of workloads, throughputs, hot vs cold CPU, fan-outs, latency,
message size and hardware
• Some interesting “odd ones out”
• Biggest
• Hottest
• Performance metrics were
• biased & coarse grain
• due to broker level collection and 24 hour sample & average & summary
• and from real workloads not benchmarks
• Hard to find correlations and make accurate predictions
• Some broad correlations and range predictions possible
Conclusions?
Top 10 clusters are “diverse”
(Paul Brebner)
Adolf Hoffmeister & Franz Kafka (Wikimedia)
© 2024 NetApp, Inc. All rights reserved.
63
• Is normal for our managed Kafka clusters
• Usage/workload varies widely for customers
• Including topics, partitions, throughput, message sizes, client
settings (e.g. batching), fan-out, latency SLAs etc
• Many bigger clusters are dedicated to very specific customer
workloads
• Higher throughput clusters are not representative of lower
throughput clusters
• Hardware varies and is optimized/customized to take into
account specific customer workloads, cost and SLA
requirements
Conclusions?
Custom Cluster Optimization and Sizing
DALL·E 3
© 2024 NetApp, Inc. All rights reserved.
64
• Performance prediction from coarse-grained
metrics feels like Déjà vu
• 2007-2017 I developed an automated approach
to Performance Modelling from distributed
application traces
• This could work for Kafka
• Instrument Apache Kafka source code with
OpenTelemetry to provide
• Kafka specific resource (CPU, IO, network) + time spans
• Run Kafka benchmarks on representative hardware
• Transform OT traces into a performance model
• Make more accurate predictions
Conclusions?
Performance Prediction
DALL·E 3
© 2024 NetApp, Inc. All rights reserved.
65
What next?
• Try us out!
• Free 30 day trial
• Developer size clusters
• www.instaclustr.com
• All my blogs (100+):
• https://instaclustr.com/paul-brebner
Thank you
© 2024 NetApp, Inc. All rights reserved.

More Related Content

Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandaries Explored)

  • 1. Why Apache Kafka Clusters Are Like Galaxies And Other Cosmic Kafka Quandaries Explored Paul Brebner Instaclustr Technology Evangelist June 5 2024 © 2024 NetApp, Inc. All rights reserved.
  • 2. Performance Engineering Track on again at Community over Code 7-10 October Denver 2024 © 2024 NetApp, Inc. All rights reserved.
  • 3. © 2024 NetApp, Inc. All rights reserved. 3 Instaclustr Managed Platform • Cloud Platform for Big Data Open Source Technologies • Free 30 day trial • Focus of this talk is on Apache Kafka®
  • 4. Centenary of Franz Kafka’s death - June 3 2024 © 2024 NetApp, Inc. All rights reserved. 4 Head of Kafka, Prague (Paul Brebner)
  • 5. Overview 1 Kafka Scalability 2 Kafka Clusters and Zipf’s Law 3 Kafka Clusters and Storage 4 Top 10 Kafka Clusters and Performance Thanks to Instaclustr colleagues for Kafka cluster data: Kafka Clusters & Storage - Alastair Daivis & Kafka Team Top 10 Clusters - Joseph Clay & Ramana Selvaratnam (Technical Operations Team)
  • 6. A note on Kafka cluster metrics Easy Performance Metrics Harder Broker Cluster All Clusters Size Metrics available Focus of our metrics collection is Per broker Not per cluster or all clusters DALL·E 3
  • 7. Part 1 Kafka Scalability (Source: Shutterstock)
  • 8. Kafka is a distributed streams processing system—it allows distributed producers to send messages to distributed consumers via a Kafka cluster. What is Kafka?
  • 9. Cluster = Brokers + Partitions Enabling Write & Read Concurrency
  • 10. Partition n Topic Partition 1 Producer Partition 2 Consumer Group Consumer Consumer Consumers share work within groups Consumer Partitions enable Consumers to share work (c.f. Amish Barn raising) within a consumer group
  • 11. Multiple groups enable message broadcasting. Messages are duplicated (c.f. clones) across groups, as each consumer group receives a copy of each message. Multiple Groups Enable Message Broadcasting Consumer Consumer Consumer Consumer Topic Partition 1 Partition 2 Partition n Producer Consumer Group Consumer Group Messages are duplicated across Consumer groups Messages are duplicated (c.f. clones) across groups, as each consumer group receives a copy of each message
  • 12. Partitions – concurrency mechanism – more is better – until it’s not You need sufficient partitions to benefit from the cluster concurrency And not too many that the replication overhead impacts overall throughput 0 0.5 1 1.5 2 2.5 1 10 100 1000 10000 Partitions vs. Throughput (M TPS) ZK TPS (M) KRAFT TPS (M) 2020 TPS (M) 2022 - Better 2020 - Worse 2022 results better due to improvements to Kafka and h/w
  • 13. © 2024 NetApp, Inc. All rights reserved. 17 • Horizontal Scalability (Brokers/Nodes) • Vertical Scalability (more/faster cores per Broker) • Hardware (cores, CPU speed/type, RAM, disk, network, etc) • Partitions + Consumers • Optimise number of Partitions • Consumer speed optimization (slow consumers are bad – high latency and too many partitions) • Kafka cluster and client configurations (many and complex) • Goals are typically • High Throughput • Fast Latency (low 10s ms) Kafka Scalability and Performance Summary (Slow consumers are a problem: Getty)
  • 14. Part 2 Kafka Clusters and Zipf’s Law – size distribution Visual size comparison of the six largest Local Group galaxies, with details (Wikipedia)
  • 15. © 2024 NetApp, Inc. All rights reserved. 19 • Distribution function • Most frequent observation is twice as common • as next and so on (i.e. 1/rank) • Long-tailed distribution • 80/20 rule (20% of people own 80% of $) • C.f. Pareto (discrete vs. continuous) • Log-log rank vs frequency/size gives approx. straight line • Common examples • Frequency of words • Wealth distribution • Animal species size • Earthquakes • City sizes • Computer systems (e.g. workload modelling, subsystem capacity) • Galaxy sizes Scaling/power law Zipf’s Law
  • 16. © 2024 NetApp, Inc. All rights reserved. 20 • Question: How large are the largest structures in the universe? • Answer: Bigger! • Zipf’s law predicted that • bigger galaxies would be detected in older parts of the universe • beyond the reach of the Hubble at the time • confirmed with the James Webb telescope observations • But what’s this got to do with Kafka? Size and Scale Predictions Apache Kafka + Galaxies? Image from NASA’s James Webb Space Telescope showing older and bigger galaxy clusters
  • 17. © 2024 NetApp, Inc. All rights reserved. 21 Raw Kafka Cluster Size Data - Summary Statistics 3 3 3 4.520702635 7.023373433 96 797 3603 1 10 100 1000 10000 Nodes/Cluster Summary Statistics (log nodes/cluster) min median mode average stdev max count sum
  • 18. © 2024 NetApp, Inc. All rights reserved. 22 Histogram (size vs count) – skewed distribution Raw Kafka Cluster Size Data 0 100 200 300 400 500 600 700 800 Total 3 4 6 8 9 12 15 18 21 24 27 30 33 36 39 48 60 72 78 96
  • 19. © 2024 NetApp, Inc. All rights reserved. 23 What is the distribution? Definitely a long-tailed power law Kafka Clusters and Zipf’s Law 0 20 40 60 80 100 120 0 100 200 300 400 500 600 700 800 900 Size Cluster Cluster Size Distribution (largest to smallest)
  • 20. © 2024 NetApp, Inc. All rights reserved. 24 Approximately Zipfian Kafka Clusters – log size vs log rank 1 10 100 1000 1 10 100 Log rank Log size Kafka Clusters - Log size vs log rank
  • 21. © 2024 NetApp, Inc. All rights reserved. 25 Can expect larger clusters (animals, galaxies etc) So What? Kafka and Zipf’s Law (1) African Elephant, 7 t Maraapunisaurus, extinct dinosaur, 150 t
  • 22. © 2024 NetApp, Inc. All rights reserved. 26 Extrapolation of size from Zipf’s law + largest observed cluster Predicted larger clusters 0.1 1 10 100 1000 1 10 100 1000 Log rank Log size Kafka Clusters - Log size vs log rank Rank Predicted larger clusters Predicted larger clusters Larger
  • 23. © 2024 NetApp, Inc. All rights reserved. 27 Estimate total nodes for more clusters Animal transportation problem So What? Kafka and Zipf’s Law (2) How many animals can fit in a boat? Public Domain
  • 24. © 2024 NetApp, Inc. All rights reserved. 28 Total weight of animals on Ark (assuming Elephant is the largest) tends to 90 tonnes If you know the size of the biggest thing you can predict the total size
  • 25. © 2024 NetApp, Inc. All rights reserved. 29 Only increases total nodes by 25% Doubling number of Kafka clusters 0 500 1000 1500 2000 2500 3000 3500 4000 4500 5000 0 200 400 600 800 1000 1200 1400 1600 1800 Cumulative total nodes 100% more clusters 25% more nodes
  • 26. Part 3 Kafka cluster storage DALL·E 3 Storage for all Kafka clusters Available from a recent project
  • 27. © 2024 NetApp, Inc. All rights reserved. 31 Correlation coefficient between size and disk = 0.9 5.6 PB Total Disk across all Kafka clusters Raw Data – total disk per cluster 0 50 100 150 200 250 300 350 400 450 500 0 20 40 60 80 100 120 Disk (TB) Nodes/cluster Disk (TB) per cluster
  • 28. © 2024 NetApp, Inc. All rights reserved. 32 • Disk space used is a function of average write rate x average message size x retention period x RF (Little’s Law) • Our metrics our total disk available, not used • Some clusters are DEV not PROD – not real workloads, and RF may be < 3 • Approximation - number of nodes as a proxy for cluster size – actual instance sizes impact capacity • Kafka log retention policy and time impact how many messages are retained • Kafka clusters are sized for peak load not average load • Some clusters may be older than others (disk can be increased) • Write vs. Read workload imbalance • Some clusters may have higher write workload rate (requiring more disk) vs. • Higher read workload rates (requiring less disk) What’s going on? 0 100 200 300 400 500 0 20 40 60 80 100 120 Disk (TB) Nodes/cluster Disk (TB) per cluster
  • 29. Part 4 Performance Metrics for Top Ten Kafka Clusters Top 10 tallest buildings (Wikipedia)
  • 30. But in reality more people are killed by horses, cows, dogs, and bees than kangaroos, sharks, snakes, crocodiles, emus, jellyfish, etc! Most Dangerous Australian Critters? Ranking can be tricky Most “dangerous” = most teeth? Most venomous? (Paul Brebner) (Wikimedia)
  • 31. © 2024 NetApp, Inc. All rights reserved. 36 • For all clusters • Size (number of nodes) and type • Disk (from extra project) • Performance Metrics are collected for all clusters • But not easily available as the focus is per-cluster operations • Requested Performance Metrics for Top Ten Clusters • What did I get? • Static (per cluster): • Nodes, Topics, Partitions • For 24 hours (per broker): • Resource Utilisation: CPU (avg, max) • Throughput: Bytes in (avg, max), Bytes out (avg, max), Messages in (avg, max) [Have to scale by number of nodes to get cluster metrics] • Performance: Producer and consumer latency (avg, p99) What metrics are available for Kafka clusters? Broker metrics need scaling to cluster metrics Variation in broker metric values 24 hour sampling loses accuracy 24 hour sample size is limited/biased Real workloads not benchmarking Ten biggest clusters by node count only Speculative Results! Warning!
  • 32. © 2024 NetApp, Inc. All rights reserved. 37 Min, Avg, Max Summary Statistics: Nodes, Topics, Partitions 27 7 2598 56.4 429.7 92145.3 96 1755 508800 1 10 100 1000 10000 100000 1000000 Nodes Topics Partitions Nodes, Topics, Partitions (Log) Min Avg Max
  • 33. © 2024 NetApp, Inc. All rights reserved. 38 Summary Statistics: CPU, GB/s in/out, Message/s (in) 2 0.396 0.12 24.5 3.14175 1.419 67.5 14.4 8.4 0.1 1 10 100 CPU Bytes in/out (GB/s) Messages in (M/s) CPU, GB/s (in+out), Messages/s (in, M/s) (Log) Min Avg Max
  • 34. © 2024 NetApp, Inc. All rights reserved. 39 Producers faster than Consumers Note that some clusters use EBS, others use SSDs (faster!) Summary Statistics: Latency (ms) 0.075 6.5 3.2925 106.65 90 700 0.01 0.1 1 10 100 1000 Producer latency (ms) Consumer latency (ms) Latency (Log) Min Avg Max
  • 35. © 2024 NetApp, Inc. All rights reserved. 40 50% of clusters have sub 50ms average latency Consumer latency distribution 0 50 100 150 200 250 300 350 1 2 3 4 5 6 7 8 9 10 Latency distribution (ms) – increasing
  • 36. © 2024 NetApp, Inc. All rights reserved. 41 150-3k Bytes Summary Statistics: Message size (Bytes) 150 1163.950072 3000 0 500 1000 1500 2000 2500 3000 3500 Message size (avg, Bytes) Message size (avg, Bytes) min avg max
  • 37. © 2024 NetApp, Inc. All rights reserved. 42 0.4 to 25 Million/s Using Average message size, compute messages out à total messages in+out 0 5 10 15 20 25 30 Msgs in+out (M/s) Msgs in+out (M/s) min avg max
  • 38. © 2024 NetApp, Inc. All rights reserved. 43 1.4 to 28 – i.e. 28 consumer groups potentially Fan out (ratio of consumer to producer messages) 0 5 10 15 20 25 30 Fan out Fan out min avg max
  • 39. © 2024 NetApp, Inc. All rights reserved. 44 Knowing metrics for top 10 clusters we can estimate total values for ALL CLUSTERS 27K topics (probably underestimate), 5.8 M partitions; 321-564 Million messages/s Assuming Zipf distribution… 27.45051596 5.886516239 321.3248554 564.9712845 1 10 100 1000 1 Grand Totals for All Kafka Clusters Topics (k) Partitions (M) Msgs in+out (avg, M/s) Msgs in+out (max, M/s)
  • 40. © 2024 NetApp, Inc. All rights reserved. 45 Nodes – 27 to 96 (1% of clusters, 564 nodes total, 16% of total nodes overall) Static data – top 10 clusters (largest on right) 27 36 36 48 51 60 60 72 78 96 0 20 40 60 80 100 120 1 2 3 4 5 6 7 8 9 10 Nodes/Cluster
  • 41. © 2024 NetApp, Inc. All rights reserved. 46 Ranges, odd ones out Biggest (10) cluster has most partitions; cluster 6 has “hottest” topics (max partitions/topic) Topics/Partitions/Nodes 7 631 13 1337 57 7 27 101 362 1755 0 500 1000 1500 2000 1 2 3 4 5 6 7 8 9 10 Topics/Cluster 6675 57672 2598 200490 11940 11394 13038 23046 85800 508800 0 100000 200000 300000 400000 500000 600000 1 2 3 4 5 6 7 8 9 10 Partitions/Cluster 0 200 400 600 800 1000 1200 1400 1600 1800 1 2 3 4 5 6 7 8 9 10 Partitions/Topic 0 200 400 600 800 1000 1200 1400 1600 1800 2000 1 2 3 4 5 6 7 8 9 10 Partitions/Node Most topics Most partitions Hottest topics
  • 42. © 2024 NetApp, Inc. All rights reserved. 47 Cluster 4 has highest max = highest topics/partitions per cluster/node Cluster 6 has highest average = highest partitions/topic (“hot” topics) These are both ”hot” clusters CPU 0% 10% 20% 30% 40% 50% 60% 70% 80% 1 2 3 4 5 6 7 8 9 10 CPU (Avg, max) CPU (Avg) CPU (max) Hottest Hot
  • 43. © 2024 NetApp, Inc. All rights reserved. 48 Topics? Theory and our Technical operations people say probably not as topics are not correlated to throughput (or size) Correlation = 0.4, some known smaller clusters with way more topics (e.g. 10,000!) Any obvious correlations to cluster size? 0 200 400 600 800 1000 1200 1400 1600 1800 2000 0 20 40 60 80 100 120 Total topics in cluster
  • 44. © 2024 NetApp, Inc. All rights reserved. 49 Partitions are related to throughput and size in theory Correlation = 0.63, and the largest cluster has most and above average partitions/nodes Size/Partition correlation? 0 100000 200000 300000 400000 500000 600000 0 20 40 60 80 100 120 Total partitions
  • 45. © 2024 NetApp, Inc. All rights reserved. 50 Average – poor correlation Size/Throughput? 0 5000000 10000000 15000000 20000000 25000000 0 20 40 60 80 100 120 Msgs in+out (avg/s)
  • 46. © 2024 NetApp, Inc. All rights reserved. 51 Max – poor correlation But avg & peak TP correlates with “hot” cluster Real workloads in 24 hour sample period don’t necessarily correlate with cluster capacities Size/Throughput? 0 5000000 10000000 15000000 20000000 25000000 30000000 0 20 40 60 80 100 120 Msgs in+out (max/s)
  • 47. © 2024 NetApp, Inc. All rights reserved. 52 • AWS ARM Graviton2 R6g high price performance for memory-intensive workloads • R6g.4xlarge 16 core (EBS) (4 clusters) • R6g.2xlarge 8 cores (EBS) (2 clusters) • AWS ARM Graviton2 Im4gn Nitro SSD for I/O intensive workloads • Im4gn.4xlarge 16 core SSD (2 clusters, including “hot” cluster) • AWS ARM Graviton2 M6g for balanced workloads • M6gd.4xlarge 16 cores SSD (1 cluster) • AWS x86 I3en for data-intensive workloads • I3en.3xlarge 12 cores SSD (1 cluster) A mix of EC2 instance types/sizes (4/5) and storage - EBS (6)/SSD (4) Top 10 clusters have heterogeneous h/w
  • 48. © 2024 NetApp, Inc. All rights reserved. 53 Good correlation (0.8) – definite increase in total cores for bigger clusters Cores per Cluster 0 200 400 600 800 1000 1200 1400 1600 1800 0 20 40 60 80 100 120 Cores per cluster Nodes per cluster Cores per cluster
  • 49. © 2024 NetApp, Inc. All rights reserved. 54 • Insights from our Techops team – thanks! • Biggest cluster (#10) • Over provisioned, 96 nodes, 1536 cores • EBS (slow) • Peak in messages/s = 1M/s • Consumer latency 200 - 400ms • Runs “cool” (18-45%) • Most partitions (0.5088 Million) • Hottest cluster (#6) • 60 nodes, 960 cores • Runs “hot” (45-55%) • But lowest consumer latency • Faster SSDs • Few topics, most partitions/topic (hot “topics”) Drill down Biggest cluster vs “hottest” cluster
  • 50. © 2024 NetApp, Inc. All rights reserved. 55 Average for cluster = 290 ms but actually a large variation across brokers Also illustrates that metrics are per broker – and have wide variability
  • 51. © 2024 NetApp, Inc. All rights reserved. 56 For target throughput how many cores and partitions are needed (in practice need both)? Can only predict a range from this data (avg=conservative; max=optimistic) Capacity Planning 6288.039891 431.386635 25583.88158 2155.566642 0 5000 10000 15000 20000 25000 30000 Msgs/s per core Msgs/s per partition Msgs/s per core and partition Avg Max
  • 52. © 2024 NetApp, Inc. All rights reserved. 57 Range: Avg (conservative), Max (optimistic) Cores for target throughput (x2 max current cluster) 0 1000 2000 3000 4000 5000 6000 7000 8000 9000 0 10 20 30 40 50 60 TPS (Million/s) vs Cores Cores (avg) Cores (max)
  • 53. © 2024 NetApp, Inc. All rights reserved. 58 Range: Avg (conservative) max (optimistic) Note: This is probably skewed due to large cluster with most partitions having low throughput and “hot” cluster with highest throughput having few partitions! Partitions for target throughput (x2 max current cluster) 0 20000 40000 60000 80000 100000 120000 0 10 20 30 40 50 60 TPS (Million/s) vs Partitions Partitions (avg) Partitions (max)
  • 54. © 2024 NetApp, Inc. All rights reserved. 59 • Lots of small clusters • Few big clusters • Even bigger clusters are likely • A wide distribution of sizes is observed • Kafka is horizontally scalable • Fits many different customer workloads • Some customers have many smaller clusters • Some clusters grow in size over time Conclusions? Kafka cluster size distribution is Zipfian DALL·E 3
  • 55. © 2024 NetApp, Inc. All rights reserved. 61 • Wide range of workloads, throughputs, hot vs cold CPU, fan-outs, latency, message size and hardware • Some interesting “odd ones out” • Biggest • Hottest • Performance metrics were • biased & coarse grain • due to broker level collection and 24 hour sample & average & summary • and from real workloads not benchmarks • Hard to find correlations and make accurate predictions • Some broad correlations and range predictions possible Conclusions? Top 10 clusters are “diverse” (Paul Brebner) Adolf Hoffmeister & Franz Kafka (Wikimedia)
  • 56. © 2024 NetApp, Inc. All rights reserved. 63 • Is normal for our managed Kafka clusters • Usage/workload varies widely for customers • Including topics, partitions, throughput, message sizes, client settings (e.g. batching), fan-out, latency SLAs etc • Many bigger clusters are dedicated to very specific customer workloads • Higher throughput clusters are not representative of lower throughput clusters • Hardware varies and is optimized/customized to take into account specific customer workloads, cost and SLA requirements Conclusions? Custom Cluster Optimization and Sizing DALL·E 3
  • 57. © 2024 NetApp, Inc. All rights reserved. 64 • Performance prediction from coarse-grained metrics feels like Déjà vu • 2007-2017 I developed an automated approach to Performance Modelling from distributed application traces • This could work for Kafka • Instrument Apache Kafka source code with OpenTelemetry to provide • Kafka specific resource (CPU, IO, network) + time spans • Run Kafka benchmarks on representative hardware • Transform OT traces into a performance model • Make more accurate predictions Conclusions? Performance Prediction DALL·E 3
  • 58. © 2024 NetApp, Inc. All rights reserved. 65 What next? • Try us out! • Free 30 day trial • Developer size clusters • www.instaclustr.com • All my blogs (100+): • https://instaclustr.com/paul-brebner
  • 59. Thank you © 2024 NetApp, Inc. All rights reserved.